<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Senko Rasic</title>
    <description>The latest articles on DEV Community by Senko Rasic (@senko).</description>
    <link>https://dev.to/senko</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F769253%2F822bcd99-5fcf-4ab5-a86f-80243a92ace8.jpg</url>
      <title>DEV Community: Senko Rasic</title>
      <link>https://dev.to/senko</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/senko"/>
    <language>en</language>
    <item>
      <title>Simple Python and Django logging</title>
      <dc:creator>Senko Rasic</dc:creator>
      <pubDate>Sat, 25 Mar 2023 16:55:25 +0000</pubDate>
      <link>https://dev.to/senko/simple-python-and-django-logging-n97</link>
      <guid>https://dev.to/senko/simple-python-and-django-logging-n97</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In this article, discuss logging in Python, specifically in the context of Django projects. Logging is essential for understanding the behavior of your application, diagnose problems, and monitor performance. More verbose logging makes it easier to pinpoint problems, but on the other hand, logging everything can lead to so manny log information that it is harder to find actual problems. The right amount of logging is different for every app and developer, but it is almost always more than "none".&lt;/p&gt;

&lt;p&gt;In Python, the &lt;code&gt;logging&lt;/code&gt; module is a standard way to implement logging functionality. The common pattern for using the logging module is by importing the &lt;code&gt;getLogger&lt;/code&gt; function and creating a logger object with the module's name:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;logging&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;getLogger&lt;/span&gt;

&lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getLogger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Something interesting happened"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The logging module provides a flexible and powerful framework for capturing log messages in your applications. You can log messages with different levels of severity, such as &lt;code&gt;DEBUG&lt;/code&gt;, &lt;code&gt;INFO&lt;/code&gt;, &lt;code&gt;WARNING&lt;/code&gt;, &lt;code&gt;ERROR&lt;/code&gt;, and &lt;code&gt;CRITICAL&lt;/code&gt;, depending on the nature of the message. Using the &lt;code&gt;getLogger&lt;/code&gt; function, you can create a logger object for each Python module, allowing you to then specify logging level and different handlers for each module.&lt;/p&gt;

&lt;p&gt;You can also use the logger functions directly on the &lt;code&gt;logging&lt;/code&gt; module. This uses the &lt;em&gt;root logger&lt;/em&gt; instead of creating a new one for each module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;logging&lt;/span&gt;
&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Something interesting happened"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is useful for small scripts, but creating per-module logger is recommended for non-trivial Python applications because it makes it easier to show or handle logs differently based on where they originated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Basic configuration for Python logging
&lt;/h2&gt;

&lt;p&gt;Basic configuration that just outputs all logs above certain severity level can be setup using &lt;code&gt;basicConfig&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;logging&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;getLogger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;basicConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;INFO&lt;/span&gt;

&lt;span class="n"&gt;basicConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getLogger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# won't show up because DEBUG is less severe than INFO
&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Not terribly interesting"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
&lt;span class="c1"&gt;# this will be shown in the log
&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Something interesting happened"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;basicConfig&lt;/code&gt; has more options such as customizing the log format or configuring where the logs will be output (if not to console).&lt;/p&gt;

&lt;h2&gt;
  
  
  Logging exception details
&lt;/h2&gt;

&lt;p&gt;The logger methods support including exception information in the log message, making it easy to provide stack traces helpful for diagnosing issues in your application. When logging a message, you can set the &lt;code&gt;exc_info&lt;/code&gt; parameter to &lt;code&gt;True&lt;/code&gt; to automatically include the exception information in the log message, like in this example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;logging&lt;/span&gt;
&lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getLogger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;ZeroDivisionError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"An error occurred while dividing by zero"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exc_info&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Simple logging in Django
&lt;/h2&gt;

&lt;p&gt;Django provides a &lt;a href="https://github.com/django/django/blob/main/django/utils/log.py#L13"&gt;default logging configuration&lt;/a&gt; out of the box. It outputs &lt;code&gt;INFO&lt;/code&gt; or more severe logs to the console only if &lt;code&gt;DEBUG&lt;/code&gt; is set to &lt;code&gt;True&lt;/code&gt; . Otherwise, it only sends email to admins on server errors.&lt;/p&gt;

&lt;p&gt;In many modern developments, it's preferrable to log everything interesting to console and leave it up to the platform (such as Docker, Systemd or Kubernetes) to take care of gathering and storing the logs. The default configuration can easily be customized by modifying the &lt;code&gt;LOGGING&lt;/code&gt; dictionary in the Django settings file.&lt;/p&gt;

&lt;p&gt;First, let's create a &lt;code&gt;LOG_LEVEL&lt;/code&gt; variable that pulls the desired log level from an environment variable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;logging&lt;/span&gt;

&lt;span class="n"&gt;LOG_LEVEL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"LOG_LEVEL"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, update the &lt;code&gt;LOGGING&lt;/code&gt; dictionary to output log messages to the console (only showing messages with severity equal to or higher than our configured &lt;code&gt;LOG_LEVEL&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;LOGGING&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;# This will leave the default Django logging behavior in place
&lt;/span&gt;    &lt;span class="s"&gt;"disable_existing_loggers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;# Custom handler config that gets log messages and outputs them to console
&lt;/span&gt;    &lt;span class="s"&gt;"handlers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;"console"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s"&gt;"class"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"logging.StreamHandler"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"level"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;LOG_LEVEL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="s"&gt;"loggers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;# Send everything to console
&lt;/span&gt;        &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s"&gt;"handlers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"console"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="s"&gt;"level"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;LOG_LEVEL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Disabling loggers
&lt;/h2&gt;

&lt;p&gt;Sometimes it's useful to disable some loggers. An example in Django is silencing the log error message when the site visitor uses an incorrect host name (ie. not among the ones whitelisted in &lt;code&gt;ALLOWED_HOSTS&lt;/code&gt; Django setting). While in debugging this might be useful, it is often an annoyance once you deploy to production. Since the public web is full with automated crawlers checking for vulnerabilities, you'll get a ton of these as soon as you set up a public-facing web app.&lt;/p&gt;

&lt;p&gt;Here's a modified logging configuration that works the same as before but ignores these messages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;LOGGING&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"disable_existing_loggers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"handlers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;"console"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"class"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"logging.StreamHandler"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="c1"&gt;# A null handler ignores the mssage
&lt;/span&gt;        &lt;span class="s"&gt;"null"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"level"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"DEBUG"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"class"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"logging.NullHandler"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="s"&gt;"loggers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s"&gt;"handlers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"console"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="s"&gt;"level"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ENV_STR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"LOG_LEVEL"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"INFO"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="s"&gt;"django.security.DisallowedHost"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;# Redirect these messages to null handler
&lt;/span&gt;            &lt;span class="s"&gt;"handlers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"null"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="c1"&gt;# Don't let them reach the root-level handler
&lt;/span&gt;            &lt;span class="s"&gt;"propagate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;p&gt;This simple configuration can be useful for many Django projects, but it is really just scratching the surface in terms of capability of Python logging module and the Django integration.&lt;/p&gt;

&lt;p&gt;If you'd like to explore more, here are a few links with more in-depth information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;a href="https://realpython.com/python-logging/"&gt;Real Python's tutorial on Python logging&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://docs.python.org/3/library/logging.html"&gt;Python logging documentation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://docs.djangoproject.com/en/stable/topics/logging/"&gt;Django logging documentation&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>django</category>
      <category>python</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Serving static files with Django</title>
      <dc:creator>Senko Rasic</dc:creator>
      <pubDate>Mon, 06 Dec 2021 11:12:03 +0000</pubDate>
      <link>https://dev.to/senko/serving-static-files-with-django-3jb8</link>
      <guid>https://dev.to/senko/serving-static-files-with-django-3jb8</guid>
      <description>&lt;p&gt;As you start your Django journey with a new project, Django's &lt;code&gt;runserver&lt;/code&gt; helpfully handles your static files for you. But the documentation emphatically tells you &lt;em&gt;not&lt;/em&gt; to do that in production: "&lt;a href="https://docs.djangoproject.com/en/3.2/howto/static-files/"&gt;This method is grossly inefficient and probably insecure, so it is unsuitable for production&lt;/a&gt;".&lt;/p&gt;

&lt;p&gt;So, what's the best way to do it?&lt;/p&gt;

&lt;h3&gt;
  
  
  Static and media files
&lt;/h3&gt;

&lt;p&gt;Here's a quick refresher for what I mean by &lt;em&gt;static&lt;/em&gt; and &lt;em&gt;media&lt;/em&gt; files in the context of Django, as they are handled differently in some cases.&lt;/p&gt;

&lt;p&gt;By &lt;em&gt;static files&lt;/em&gt;, I mean files that are part of your project (committed to your code repository) and are served as-is to your visitors. These may be images, CSS files, JavaScript files, PDFs, audio or video files - anything that's served as is, but is built-in to your project.&lt;/p&gt;

&lt;p&gt;By &lt;em&gt;media files&lt;/em&gt;, I mean files that are uploaded by your users and afterward served as is. A typical example are photos uploaded by your users.&lt;/p&gt;

&lt;h3&gt;
  
  
  Local and cloud storage
&lt;/h3&gt;

&lt;p&gt;Both static files and media files can be stored either locally, on the same server that hosts the Django project, or somewhere else, for example on a cloud storage service.&lt;/p&gt;

&lt;p&gt;Back in the day when most projects were deployed on a dedicated or virtual server, most files were stored locally. Today, if using services or tools that treat the deployed server as an immutable (read-only) image, this is no longer possible. Examples of this approach are Heroku and docker. In this case, storing media files locally is impossible, requiring the use of some kind of cloud storage.&lt;/p&gt;

&lt;h3&gt;
  
  
  Serving via a web server
&lt;/h3&gt;

&lt;p&gt;If your Django instance sits behind a web server such as Nginx or Apache and your files are stored locally, the webserver can serve them directly.&lt;/p&gt;

&lt;p&gt;Assuming your static files are located (collected) in &lt;code&gt;/var/app/static&lt;/code&gt; (so that's your &lt;code&gt;STATIC_ROOT&lt;/code&gt; in Django settings) and served under &lt;code&gt;/static/&lt;/code&gt; prefix (that's your &lt;code&gt;STATIC_URL&lt;/code&gt; in Django), here's an example Nginx config to serve them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;location /static/ {
    root /var/app;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you'd also want to serve media files, assuming &lt;code&gt;MEDIA_URL&lt;/code&gt; is &lt;code&gt;/media/&lt;/code&gt; and &lt;code&gt;MEDIA_ROOT&lt;/code&gt; is &lt;code&gt;/var/app/media&lt;/code&gt;, you'd add another block:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;location /media/ {
    root /var/app;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As an aside: since Nginx appends the URI path to the document root, it's useful to have the &lt;code&gt;STATIC_ROOT&lt;/code&gt; end in &lt;code&gt;STATIC_URL&lt;/code&gt; and &lt;code&gt;MEDIA_ROOT&lt;/code&gt; end in &lt;code&gt;MEDIA_URL&lt;/code&gt; - it makes configuration simpler.&lt;/p&gt;

&lt;p&gt;For Apache, the configuration would look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DocumentRoot /var/app;

Alias /static /var/app/static;
Alias /media /var/app/media;

&amp;lt;Directory /var/app&amp;gt;
  Deny from all
&amp;lt;/Directory&amp;gt;
&amp;lt;Directory /var/app/static&amp;gt;
  Allow from all
&amp;lt;/Directory&amp;gt;
&amp;lt;Directory /var/app/media&amp;gt;
  Allow from all
&amp;lt;/Directory&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that both examples are minimal and ignore things like compression, setting expiration headers, and so on.&lt;/p&gt;

&lt;h3&gt;
  
  
  Serving from cloud storage
&lt;/h3&gt;

&lt;p&gt;If you're using cloud storage such as Amazon S3, you probably use the excellent &lt;a href="https://pypi.org/project/django-storages/"&gt;django-storages&lt;/a&gt;. It makes serving static files directly from the cloud service easy. In your Django settings file, you just set your &lt;code&gt;STATICFILES_STORAGE&lt;/code&gt; (for static files) and &lt;code&gt;DEFAULT_FILE_STORAGE&lt;/code&gt; (for media files) to your preferred backend.&lt;/p&gt;

&lt;p&gt;Example for Amazon S3:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;STATICFILES_STORAGE = 'storages.backends.s3boto3.S3StaticStorage'
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you call &lt;code&gt;manage.py collectstatic&lt;/code&gt; as part of your deployment procedure, the storages engine will upload all static files to S3. Media files will be uploaded to S3 when you try to save them server-side.&lt;/p&gt;

&lt;p&gt;Again, note that this is a minimalistic example - see &lt;a href="https://django-storages.readthedocs.io/en/latest/index.html"&gt;django-storages documentation&lt;/a&gt; for detailed installation instructions and all the options.&lt;/p&gt;

&lt;h3&gt;
  
  
  Serving static files via whitenoise
&lt;/h3&gt;

&lt;p&gt;If you're serving your media files from a cloud service, you still have the option to serve &lt;em&gt;static&lt;/em&gt; files locally. You may want to do that to simplify deployment or rollback procedure or for some other reason.&lt;/p&gt;

&lt;p&gt;If you're behind a web server you can use it to serve static files as explained previously. But if you're using docker or deployed to a platform like Heroku, you may not be able to do that. Instead, you can serve them directly from Django, using &lt;a href="https://pypi.org/project/whitenoise/"&gt;whitenoise&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Enabling whitenoise is as simple as adding it to the list of middlewares in Django settings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MIDDLEWARE = [
  'whitenoise.middleware.WhiteNoiseMiddleware',
  # ...
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note the placement of middleware matters - be sure to &lt;a href="https://whitenoise.evans.io/en/stable/"&gt;read the docs&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Compression and caching
&lt;/h3&gt;

&lt;p&gt;Both &lt;code&gt;django-storages&lt;/code&gt; and &lt;code&gt;whitenoise&lt;/code&gt; support compression and manifest storage for your content.&lt;/p&gt;

&lt;p&gt;Compressing your static files where it makes sense can dramatically decrease their size and thus load time of your pages. CSS and JavaScript files are good candidates for compression, while images, audio, and video files are already compressed.&lt;/p&gt;

&lt;p&gt;If you enable manifest storage, the collectstatic step will rename the files to a unique name. For example, &lt;code&gt;script.js&lt;/code&gt; might be renamed to &lt;code&gt;script.f9f204dfa.js&lt;/code&gt;, where &lt;code&gt;f9f204dfa&lt;/code&gt; is a &lt;em&gt;manifest&lt;/em&gt;, or checksum, of the file contents. The manifest changes each time the file contents change, meaning each change to a file will generate a new, unique, name.&lt;/p&gt;

&lt;p&gt;This, in turn, means we can tell the browser (and a CDN if you use one) to cache files forever. When you deploy a new version of your project that changes the file, a new file name will be used and the browser (or a CDN) will know to fetch the new file.&lt;/p&gt;

&lt;p&gt;An important caveat here is that you must always access the static files using the &lt;code&gt;{% static %}&lt;/code&gt; template tag. The template tag will rewrite the name to use the actual (current) manifest. If you hardcode path like &lt;code&gt;script.js&lt;/code&gt; in your template, it will not be found.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use with CDN
&lt;/h3&gt;

&lt;p&gt;All of the above approaches can be used irrespective of whether you're using a content delivery network (CDN) or not. If you do use CDN, using manifest storage will help you even more.&lt;/p&gt;

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