<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Gustavo Maciel on Medium]]></title>
        <description><![CDATA[Stories by Gustavo Maciel on Medium]]></description>
        <link>https://medium.com/@gustavocmaciel?source=rss-e7ee90b66451------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/proxy/1*TGH72Nnw24QL3iV9IOm4VA.png</url>
            <title>Stories by Gustavo Maciel on Medium</title>
            <link>https://medium.com/@gustavocmaciel?source=rss-e7ee90b66451------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Fri, 29 May 2026 18:27:03 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@gustavocmaciel/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Configuring Nginx as a Reverse Proxy for a Go App in Docker.]]></title>
            <link>https://medium.com/geekculture/configuring-nginx-as-a-reverse-proxy-for-a-go-app-in-docker-fa7fd6a66568?source=rss-e7ee90b66451------2</link>
            <guid isPermaLink="false">https://medium.com/p/fa7fd6a66568</guid>
            <category><![CDATA[docker-compose]]></category>
            <category><![CDATA[reverse-proxy]]></category>
            <category><![CDATA[golang]]></category>
            <category><![CDATA[docker]]></category>
            <category><![CDATA[nginx]]></category>
            <dc:creator><![CDATA[Gustavo Maciel]]></dc:creator>
            <pubDate>Mon, 07 Oct 2024 11:27:06 GMT</pubDate>
            <atom:updated>2024-10-07T11:27:06.698Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*6vyUkOfgFlX1MTgcNa_k6w.png" /><figcaption>Nginx as a Reverse Proxy</figcaption></figure><p>If you have ever worked with Nginx, you know that one of its main use cases is to place it at the front of your application and use it as a reverse proxy to enhance security, performance, and scalability — among other benefits. In this guide, we will configure a Docker architecture where a Go application communicates with the outside world through an Nginx server, with each component running in its own container.</p><p><em>I’m assuming you know how to use Docker and you are also familiar with Docker Compose. Otherwise, this guide might be a bit challenging to follow, so I recommend reviewing the basics of Docker and Docker Compose before proceeding.</em></p><h4>Project Organization</h4><p>Let’s start with the project organization. For this example, I have a simple REST API written in Go with only one endpoint. I’ve simplified it since this guide focuses more on the Docker setup rather than on building APIs in Go.</p><pre>nginx-go/<br>├── compose.yml<br>├── go-app/<br>│   ├── cmd/<br>│   │   └── main.go<br>│   └── Dockerfile<br>└── nginx/<br>    ├── Dockerfile<br>    └── nginx.conf</pre><h4>Go App Configuration</h4><p>We’ll take a look at the go-app/cmd/main.go first</p><pre>package main<br><br>import (<br>   &quot;fmt&quot;<br>   &quot;log&quot;<br>   &quot;net/http&quot;<br>)<br><br>func indexHandler(w http.ResponseWriter, r *http.Request) {<br>   fmt.Fprint(w, &quot;Nginx server as a reverse proxy!&quot;)<br>}<br><br>func main() {<br>   http.HandleFunc(&quot;/&quot;, indexHandler)<br>   log.Fatal(http.ListenAndServe(&quot;:3000&quot;, nil))<br>}</pre><p>Basically, it contains the configuration for starting the app and handling incoming requests properly. As mentioned, it’s a simplified version, with all the code located in the main.go file, which we would typically avoid in more complex scenarios. However, since the focus here is on Docker, we won’t go deep into the Go code.</p><p>Taking a look at the go-app/Dockerfile</p><pre>FROM golang:1.21.3 AS builder<br><br>WORKDIR /app<br><br>COPY /cmd/main.go ./cmd/<br><br>RUN go mod init go-app<br><br>RUN CGO_ENABLED=0 GOOS=linux go build -o /go-app ./cmd/main.go<br><br>FROM scratch<br><br>WORKDIR /<br><br>COPY --from=builder /go-app .<br><br>EXPOSE 3000<br><br>CMD [ &quot;/go-app&quot; ]</pre><p>The purpose of this Dockerfile is to build the Go binary to run on a lightweight image. It’s as simple as that.</p><p>Note that port 3000 in the Go container is exposed within the network, but <strong>we won’t expose it to the outside</strong>.</p><h4>Nginx configuration</h4><p>Here’s where things start to get more interesting. To place Nginx in front of the Go application, we need to configure the Dockerfile for Nginx (as usual) and also configure the nginx.conf file to reroute the requests received by the Nginx server to the Go application running on port 3000. Let’s look at the Dockerfile for Nginx.</p><p><strong>nginx/Dockerfile</strong></p><pre>FROM nginx:latest<br><br>EXPOSE 80<br><br>ENTRYPOINT [&quot;/docker-entrypoint.sh&quot;]<br>CMD [&quot;nginx&quot;, &quot;-g&quot;, &quot;daemon off;&quot;]</pre><p>It typically builds the image and starts the server exposing port 80.</p><p><strong>nginx/nginx.conf</strong></p><pre>server {<br>    listen 80;<br><br>    server_name localhost;<br><br>    location / {<br>            proxy_pass http://go-app:3000;<br>            proxy_http_version 1.1;<br>            proxy_set_header Upgrade $http_upgrade;<br>            proxy_set_header Connection &#39;upgrade&#39;;<br>            proxy_set_header Host $host;<br>            proxy_cache_bypass $http_upgrade;<br>        }<br>}</pre><p>The nginx.conf file is the <em>trickiest</em> part, as it specifies where to send the requests received on port 80. Here, we set proxy_pass to route the requests to http://go-app:3000. Since Docker containers know the names of other containers (due to built-in DNS resolution), they can communicate with each other using just the name and port (http://go-app:3000).</p><h4>Docker Compose Configuration</h4><p>We’re using Docker Compose to automate the process of setting up and running the containers. In this compose.yml file, we are configuring the Go app and the Nginx server to run on the same go-network, which is also defined in the compose file. In the configuration for the Nginx container, we&#39;re mapping port 8080 from our system to port 80 of the Nginx container, so we can easily access it from localhost:8080.</p><p>We also create a volume to map the nginx/ folder in our working directory to the /etc/nginx/conf.d/ folder inside the container, so our nginx.conf file can be placed inside the Nginx container.</p><p><strong>compose.yml</strong></p><pre>version: &#39;3&#39;<br><br>services: <br><br>  app:<br>    build: <br>      context: go-app<br>    container_name: go-app<br>    networks: <br>      - go-network<br><br>  nginx:<br>    build: <br>      context: nginx<br>    container_name: nginx<br>    volumes: <br>      - ./nginx:/etc/nginx/conf.d/<br>    networks: <br>      - go-network<br>    ports: <br>      - &quot;8080:80&quot;<br>    depends_on:<br>      - app<br><br>networks: <br>  go-network:<br>    driver: bridge</pre><h4>Running Everything</h4><p>With all that done, we can now run these containers in detached mode and check if everything went well. We will open the terminal and run the following command:</p><pre>docker compose up -d</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*fALVXlLABixMptcfL2MYoA.png" /></figure><p>No errors occurred, so this looks promising. Now, we can open the browser and try to access localhost:8080 to see the result.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*3PWOWI_6EKZdKZrv1OhyXA.png" /></figure><p>Great! We can see the response from the index endpoint that we configured. Since the Go app is running on port 3000, we know that Nginx is correctly redirecting from its own port 80, which we bound to our 8080. This means everything is working as expected.</p><h4>Conclusion</h4><p>I hope this guide has given you insight into how you can easily configure your Docker environment to use Nginx as a reverse proxy for a Go app. I’ve chosen to use Go as an example, but the configuration structure is almost the same for any app written in any language. So, if you want, this setup can be adapted without much hassle.</p><p><em>You can check the source code for this guide on </em><a href="https://github.com/gustavocmaciel/nginx-go"><em>Github</em></a><em>.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=fa7fd6a66568" width="1" height="1" alt=""><hr><p><a href="https://medium.com/geekculture/configuring-nginx-as-a-reverse-proxy-for-a-go-app-in-docker-fa7fd6a66568">Configuring Nginx as a Reverse Proxy for a Go App in Docker.</a> was originally published in <a href="https://medium.com/geekculture">Geek Culture</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to Deploy a Django App on Heroku]]></title>
            <link>https://medium.com/geekculture/how-to-deploy-a-django-app-on-heroku-4d696b458272?source=rss-e7ee90b66451------2</link>
            <guid isPermaLink="false">https://medium.com/p/4d696b458272</guid>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[postgres]]></category>
            <category><![CDATA[heroku]]></category>
            <category><![CDATA[cloud-computing]]></category>
            <category><![CDATA[django]]></category>
            <dc:creator><![CDATA[Gustavo Maciel]]></dc:creator>
            <pubDate>Fri, 22 Jan 2021 16:32:54 GMT</pubDate>
            <atom:updated>2022-06-16T14:03:44.723Z</atom:updated>
            <content:encoded><![CDATA[<h4>A step by step guide.</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*PK2u6RWGJyiEkpeN" /><figcaption>Photo by <a href="https://unsplash.com/@jstrippa?utm_source=medium&amp;utm_medium=referral">James Harrison</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>This is the moment where <em>some</em> developers feels lost: the deployment time. Something that should be trivial, sometimes isn’t, but it shouldn’t be so hard at all — in fact, it is not. Hopefully, by the end of this guide, you’ll be able to host your Django project on Heroku, which is a great cloud service, especially because Heroku is a cloud PaaS (Platform as a Service) and also offers a free plan with which you can deploy your project with a full database provision.</p><p>Before you start, you have to make some configurations on your project to make it work on the server. There’s a difference between development and production and this tutorial will guide you thru it.</p><p><em>Note: I’m assuming you’re using a </em><strong><em>virtual environment</em></strong><em>. You also have to create a </em><strong><em>requirements.txt</em></strong><em> file and add all the python packages required to run your web application (including Django itself) to it. If you haven’t created this file, you can do this running the following command on your terminal:</em></p><pre><em>pip3 freeze &gt; requirements.txt</em></pre><p>First of all, you need a <strong>Procfile</strong> , which is used to declare the application process type. This file must be located in your project’s root directory. You can create one running the following command on your terminal:</p><pre><em>echo &quot;</em><em>web: gunicorn PROJECT_NAME.wsgi&quot; &gt; Procfile</em></pre><p>Where <strong>PROJECT_NAME</strong> is the name of your Django project.</p><p>Next, you need to install some packages to make the application work on the server. These are the required packages:</p><ul><li>gunicorn (an application server)</li><li>dj-database-url (for the database)</li><li>whitenoise (for serving static files)</li><li>psycopg2 (for postgres database)</li></ul><p>To install them all in once, just go to your terminal and run:</p><p><em>pip3 install gunicorn dj-database-url whitenoise psycopg2-binary</em></p><p>After the installation has completed, add them to your requirements.txt:</p><p><em>pip3 freeze &gt; requirements.txt</em></p><p>Now you have to create a <strong>runtime.txt</strong> file in your root directory — just like the Procfile — and add the python version you’re using. To check which python version you&#39;re using, run:</p><pre><em>python3 --version</em></pre><p>The output will be something like this:</p><pre><em>Python 3.8.5</em></pre><p>Since I’m using <strong>Python 3.8.5</strong>, to create the <em>runtime </em>file and add the python version, I can just run the following command:</p><pre><em>echo &quot;python-3.8.5&quot; &gt; runtime.txt</em></pre><p>You should also create a <strong>.gitignore</strong> file and add the <em>sqlite</em> <em>database </em>to it as long as the <em>virtual environment folder</em>. To do this run:</p><pre>echo &quot;db.sqlite3&quot; &gt; .gitignore<br>echo &quot;env/&quot; &gt;&gt; .gitignore</pre><p>where <strong>env/</strong> is the name of your virtual environment.</p><p><strong><em>Note: The second command should use two arrows ( &gt;&gt;) instead of just one ( &gt;), because you’re appending to the file. Using one single arrow ( &gt;) would replace the content of the file with the new one.</em></strong></p><p>Now you have to configure some of the project files to make the application work on the server. First, go to the <strong>settings.py</strong> file and do the following:</p><ul><li>To allow <strong>whitenoise</strong> to handle the static files, you have to add the following line to the list of <strong>INSTALLED_APPS</strong>:</li></ul><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/c718aa012cbd3e4d2012c71cafc54046/href">https://medium.com/media/c718aa012cbd3e4d2012c71cafc54046/href</a></iframe><ul><li>Add the following line to <strong>ALLOWED_HOSTS</strong> list:</li></ul><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/e74b4c91728fd7e95cd0a9e843bdfd26/href">https://medium.com/media/e74b4c91728fd7e95cd0a9e843bdfd26/href</a></iframe><ul><li>You also have to set <strong>Debug</strong> to <strong>False</strong>:</li></ul><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b5b16f86599ed1e59b9ed548ae1f2168/href">https://medium.com/media/b5b16f86599ed1e59b9ed548ae1f2168/href</a></iframe><ul><li>Now you have to add <strong>whitenoise</strong> to the <strong>MIDDLEWARE</strong> list, but this part is tricky —<em> </em><strong><em>the whitenoise middleware should be included right after the</em></strong><em> </em><strong><em>django.middleware.security.SecurityMiddleware</em></strong></li></ul><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/14ebcfe316b1252d37abb1779d3f7c26/href">https://medium.com/media/14ebcfe316b1252d37abb1779d3f7c26/href</a></iframe><ul><li>Now you have to add the configuration for the static files. At the bottom of your <strong>settings.py</strong> , add these lines:</li></ul><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/acff21cec451b0477e1e22df6949b612/href">https://medium.com/media/acff21cec451b0477e1e22df6949b612/href</a></iframe><h4>Finally, but not least, you have to configure the Postgres database</h4><ul><li>Right after the <strong>DATABASES</strong> dictionary, add the following lines:</li></ul><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/9ab82155f84207b1f09e98945718de49/href">https://medium.com/media/9ab82155f84207b1f09e98945718de49/href</a></iframe><p>Yeah, that was a lot, but now your app is ready.</p><h4>Heroku Part</h4><p>Now you have to set up your Heroku account (if you don’t have one). It can be done <a href="https://signup.heroku.com/">here</a>.</p><p>You also have to install the <strong>Heroku-CLI</strong>. If you are on Mac, you can install running:</p><pre><em>brew install heroku/brew/heroku</em></pre><p>If you are on Linux (Ubuntu), run:</p><pre><em>sudo snap install heroku --classic</em></pre><p>On Windows, you have to download the installer, which can be done from <a href="https://devcenter.heroku.com/articles/getting-started-with-python#set-up">this page</a>.</p><p>After the installation has finished, you have to log in. On your terminal, run:</p><pre><em>heroku login</em></pre><p>…and follow the instructions.</p><p>Now, to create a new app, run:</p><pre><em>heroku create PROJECT_NAME</em></pre><p>where <strong>PROJECT_NAME</strong> is the name of your project.</p><p>Next, commit your changes and push your project to deploy your app:</p><pre><em>git add -A<br>git commit -am &quot;commit message&quot;<br>git push heroku master</em></pre><p>And that’s it, congrats! You just deployed your app. But you still have to configure the database. To add the Postgres add-on, you have to run:</p><pre><em>heroku addons:create heroku-postgresql:hobby-dev --app PROJECT_NAME</em></pre><p>where <strong>PROJECT_NAME</strong> is the name of your project.</p><p>Now you migrate to the database:</p><pre><em>heroku run python manage.py migrate</em></pre><p>Finally, you have to create a superuser:</p><pre><em>heroku run python manage.py createsuperuser</em></pre><p>If everything went well, you’ve successfully deployed your Django app. To open your app, run:</p><pre><em>heroku open</em></pre><h3>All done!</h3><p>If you got this far, congratulations! You not only deployed your project to Heroku but also learned a lot of new stuff — I hope so.</p><p>Thanks for reading!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4d696b458272" width="1" height="1" alt=""><hr><p><a href="https://medium.com/geekculture/how-to-deploy-a-django-app-on-heroku-4d696b458272">How to Deploy a Django App on Heroku</a> was originally published in <a href="https://medium.com/geekculture">Geek Culture</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to Style Your Django Forms]]></title>
            <link>https://medium.com/swlh/how-to-style-your-django-forms-7e8463aae4fa?source=rss-e7ee90b66451------2</link>
            <guid isPermaLink="false">https://medium.com/p/7e8463aae4fa</guid>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[css]]></category>
            <category><![CDATA[bootstrap]]></category>
            <category><![CDATA[django]]></category>
            <category><![CDATA[html]]></category>
            <dc:creator><![CDATA[Gustavo Maciel]]></dc:creator>
            <pubDate>Wed, 13 Jan 2021 14:22:12 GMT</pubDate>
            <atom:updated>2022-06-16T13:46:13.862Z</atom:updated>
            <content:encoded><![CDATA[<h4>Spoiler: You have to use widgets.</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*r8aS75L1KAHJ2sZtGgFxQQ.jpeg" /><figcaption>Photo by <a href="https://unsplash.com/@goshua13?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Joshua Aragon</a> on <a href="https://unsplash.com/s/photos/laptop?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></figcaption></figure><p>Everyone who uses Django knows how good the Django forms can be. But when you are using it for the first time, a question araises: How can I style it? How can I add a class?</p><p>Well, there’s a way (quite simple actually), you have to use widgets.</p><p>But what is that exactly? Let’s see the definition of widget, according to <a href="https://docs.djangoproject.com/en/3.1/ref/forms/widgets/#module-django.forms.widgets">Django Docs</a>:</p><blockquote>A widget is Django’s representation of an HTML input element. The widget handles the rendering of the HTML, and the extraction of data from a GET/POST dictionary that corresponds to the widget.</blockquote><p>In other words, widget is just a way to define how that content will be rendered as HTML. So, for example, a <strong>CharField</strong> has a default widget of <strong>TextInput</strong> that renders as <strong>&lt;input type=&quot;text&quot;&gt;</strong> .</p><p>But widgets are customizable, so you can also set things like the size of that <strong>textarea</strong> or if that field is going to be a <em>required</em><strong> </strong>field and so goes on…</p><p>So, let’s try to build a example to show the widgets in action.</p><p>Suppose we have a form called <strong>UserInfoForm</strong> to get the name of the user and also his email.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/feebe04c8e12cf1a718ffeca3ca0d24e/href">https://medium.com/media/feebe04c8e12cf1a718ffeca3ca0d24e/href</a></iframe><p>And the HTML looks like this:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/8785e1ad140c46c6281c03f4d63eacc2/href">https://medium.com/media/8785e1ad140c46c6281c03f4d63eacc2/href</a></iframe><p>Right now this form is using the default widgets, and it doesn’t have any style, so, basically, it looks like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/627/1*jLjPDZPB_WMMV9ApP9ZDWw.png" /></figure><p>So, to change it, we need to customize the appearance. You can customize widgets in two ways — it can be via <strong>widget instance</strong> or <strong>widget class</strong>. For this first example, I’ll be using widget instance. Basically you have to use the <strong>Widget.attrs</strong> argument, which is a dictionary containing HTML attributes to be set to the rendered widget, like the example below:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/1427f7e0d6318439992996b537ebd509/href">https://medium.com/media/1427f7e0d6318439992996b537ebd509/href</a></iframe><p>And then we can see the <strong>attrs</strong> dictionary taking <strong>placeholder</strong> as a key an the <strong>Name</strong> as value as long with the <strong>style </strong>as key and the <strong>width: 300px;</strong> as value.</p><p>And this is the result:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/780/1*4PSF4metf8OgrwbeC2U06A.png" /></figure><p>Much better, right? But it still can be improved, we can add a Bootstrap class to it. And we can do this by declaring a class inside the <strong>attrs</strong> dictionary.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/cbc4538dd7d84ff80580b992b10e2111/href">https://medium.com/media/cbc4538dd7d84ff80580b992b10e2111/href</a></iframe><p>We’ve added a Bootstrap class and this is the result.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/618/1*DKooHuBORmq8uBvBUwMakw.png" /></figure><p>Now it’s good!</p><p>But usually when we are using Django forms, those forms are related to some model — and right now, this form isn’t. To that happen, we need to make a few more changes. We’re going to use the <strong>widget class</strong> now. The widget class has a basic attribute <strong>attrs</strong>, just like the the example above. We also have to add a new class called <strong>Meta</strong> and specify the name of the model this form is related to, the fields we want to have and finally the widgets for those fields.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/6157b95bbde20066769ca6ef9a6eb24c/href">https://medium.com/media/6157b95bbde20066769ca6ef9a6eb24c/href</a></iframe><p>So, what’s going on here? The <strong>CharField</strong> and the <strong>EmailField</strong> that we were using before are <strong>Built-in Field classes</strong>, but if we are going to work with widgets class, we need to use the <strong>Built-in widgets</strong> (and these widgets are <strong>TextInput</strong> and <strong>EmailInput</strong>, for this example). They will work the same in the end but the configuration will be slightly different.</p><p>This is basically the same form as before, it didn’t change the way it gets rendered or anything, but now this form is <em>connected</em> to the <strong>User</strong> model, which is the one that was created to store the user info.</p><p>And the HTML looks like this:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/0c4188920c9dfa57e204e124e630b3cb/href">https://medium.com/media/0c4188920c9dfa57e204e124e630b3cb/href</a></iframe><p>The final result:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/629/1*1ZwEvdAllKVQ_5_rh8vvMg.png" /></figure><h3><strong>Conclusion</strong></h3><p>Learning how to use widgets is great because now you know how to make those forms look better and how simple it is to add a Bootstrap class, which can be handy.</p><p>You can check the source code of this project on <a href="https://github.com/gustavocmaciel/django-widgets-tutorial">Github</a>.</p><p>Of course there’s much more you can do with widgets. And the best place to learn is from the official documentation — <em>aka</em> <a href="https://docs.djangoproject.com/en/3.1/ref/forms/widgets/">Django docs</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=7e8463aae4fa" width="1" height="1" alt=""><hr><p><a href="https://medium.com/swlh/how-to-style-your-django-forms-7e8463aae4fa">How to Style Your Django Forms</a> was originally published in <a href="https://medium.com/swlh">The Startup</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>