From bb2e47601b3a3e2c890455da1ada19c320c181bb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nazar=C3=A9=20da=20Piedade?=
Date: Sat, 25 Mar 2023 09:48:43 +0000
Subject: [PATCH 01/30] docs(pt): prepare ground to translate understanding
django book
---
.../2020-01-08-browser-to-django.pt.md | 831 ++++++++++
.../2020-01-22-urls-lead-way.pt.md | 878 ++++++++++
.../2020-03-03-views-on-views.pt.md | 963 +++++++++++
...2020-04-02-templates-user-interfaces.pt.md | 1355 +++++++++++++++
.../2020-05-05-user-interaction-forms.pt.md | 887 ++++++++++
.../2020-06-25-store-data-with-models.pt.md | 1449 +++++++++++++++++
...2020-08-26-administer-all-the-things.pt.md | 1008 ++++++++++++
...2020-09-29-anatomy-of-an-application.pt.md | 638 ++++++++
.../2020-11-04-user-authentication.pt.md | 957 +++++++++++
.../2020-12-16-middleware-do-you-go.pt.md | 781 +++++++++
.../2021-01-06-serving-static-files.pt.md | 800 +++++++++
.../2021-02-22-test-your-apps.pt.md | 1142 +++++++++++++
.../2021-03-23-deploy-site-live.pt.md | 816 ++++++++++
.../2021-05-21-sessions.pt.md | 519 ++++++
.../2021-07-15-settings.pt.md | 838 ++++++++++
.../2021-09-14-media-files.pt.md | 543 ++++++
.../2021-11-04-command-apps.pt.md | 708 ++++++++
.../2022-01-19-go-fast.pt.md | 1163 +++++++++++++
.../2022-03-10-secure-apps.pt.md | 622 +++++++
...2022-05-31-debugging-tips-techniques.pt.md | 767 +++++++++
content/understand-django/_index.pt.md | 48 +
21 files changed, 17713 insertions(+)
create mode 100644 content/understand-django/2020-01-08-browser-to-django.pt.md
create mode 100644 content/understand-django/2020-01-22-urls-lead-way.pt.md
create mode 100644 content/understand-django/2020-03-03-views-on-views.pt.md
create mode 100644 content/understand-django/2020-04-02-templates-user-interfaces.pt.md
create mode 100644 content/understand-django/2020-05-05-user-interaction-forms.pt.md
create mode 100644 content/understand-django/2020-06-25-store-data-with-models.pt.md
create mode 100644 content/understand-django/2020-08-26-administer-all-the-things.pt.md
create mode 100644 content/understand-django/2020-09-29-anatomy-of-an-application.pt.md
create mode 100644 content/understand-django/2020-11-04-user-authentication.pt.md
create mode 100644 content/understand-django/2020-12-16-middleware-do-you-go.pt.md
create mode 100644 content/understand-django/2021-01-06-serving-static-files.pt.md
create mode 100644 content/understand-django/2021-02-22-test-your-apps.pt.md
create mode 100644 content/understand-django/2021-03-23-deploy-site-live.pt.md
create mode 100644 content/understand-django/2021-05-21-sessions.pt.md
create mode 100644 content/understand-django/2021-07-15-settings.pt.md
create mode 100644 content/understand-django/2021-09-14-media-files.pt.md
create mode 100644 content/understand-django/2021-11-04-command-apps.pt.md
create mode 100644 content/understand-django/2022-01-19-go-fast.pt.md
create mode 100644 content/understand-django/2022-03-10-secure-apps.pt.md
create mode 100644 content/understand-django/2022-05-31-debugging-tips-techniques.pt.md
create mode 100644 content/understand-django/_index.pt.md
diff --git a/content/understand-django/2020-01-08-browser-to-django.pt.md b/content/understand-django/2020-01-08-browser-to-django.pt.md
new file mode 100644
index 00000000..2fb950a0
--- /dev/null
+++ b/content/understand-django/2020-01-08-browser-to-django.pt.md
@@ -0,0 +1,831 @@
+---
+title: "From Browser To Django"
+description: >-
+ Django helps you build websites
+ in Python.
+ How does it work?
+ In this series,
+ we'll explore Django
+ from top to bottom
+ to show you how to build
+ the website you've wanted.
+ We'll start
+ from the beginning
+ with the browser.
+image: img/django.png
+type: post
+categories:
+ - Python
+ - Django
+tags:
+ - Python
+ - Django
+
+---
+
+Maybe you have heard about
+{{< extlink "https://www.djangoproject.com/" "Django" >}}
+and that it can help you build websites.
+You might be new to Python,
+new to web development,
+or new to programming.
+
+{{< web >}}
+This new series,
+[Understand Django]({{< ref "/understand-django/_index.md" >}}),
+will show you what Django is all about.
+Throughout this series,
+{{< /web >}}
+{{< book >}}
+This book
+will show you what Django is all about.
+In the following chapters,
+{{< /book >}}
+I will reveal how Django is a powerful tool
+that can unlock the potential
+of anyone interested
+in making applications
+on the internet.
+Django is used
+by companies
+like Instagram,
+Eventbrite,
+Disqus,
+and
+Udemy,
+and is also a great tool
+for individuals like you.
+
+We're going to take a high-level approach to learning Django.
+Rather than starting
+at the bottom
+with all the pieces
+of Django,
+I'll give you the big picture,
+then explore each layer in more detail
+to reveal how much Django does
+for developers
+and the power Django has
+under the hood.
+
+Let's get started
+from the very top
+of a user's internet experience:
+at the web browser.
+
+{{< understand-django-series "browser" >}}
+
+## Making A Browser Request
+
+Django is a web framework,
+but what the heck does that even mean?
+How do websites work?
+I'm not going
+to be able to walk
+through all the details,
+{{< web >}}
+but this post
+{{< /web >}}
+{{< book >}}
+but this chapter
+{{< /book >}}
+will lay down the breadcrumbs
+to build your understanding.
+We'll look at the way your web browser requests data
+from the internet
+and the "plumbing" needed
+to make that work.
+Equipped with the key words
+and acronyms found in this chapter,
+you should be able
+to start your own research
+on these topics.
+
+The internet works
+by fulfilling a user's desire
+for sending and receiving information.
+That "information" takes many different forms.
+It might be:
+
+* Cat videos on YouTube
+* Political ramblings from social media
+* Profiles of other people on dating sites
+
+Whatever people are looking for,
+the information is transferred
+via the same mechanisms.
+In internet-speak,
+all types of information and data
+fall under the name *resource*.
+
+The way we get resources are with
+{{< extlink "https://en.wikipedia.org/wiki/URL" "Uniform Resource Locators" >}}
+or URLs,
+for short.
+You know what URLs are,
+even if you didn't know them by name.
+
+* {{< extlink "https://en.wikipedia.org/" "https://en.wikipedia.org/" >}}
+* {{< extlink "https://www.djangoproject.com/" "https://www.djangoproject.com/" >}}
+* {{< extlink "https://www.mattlayman.com/img/django.png" "https://www.mattlayman.com/img/django.png" >}}
+
+These are all examples
+of URLs.
+Often we call them web addresses
+because they're very similar to postal addresses.
+A URL is the address
+of some resource
+on the internet.
+When you hit *Enter*
+on the address bar
+of your browser,
+you're saying
+"Please browser,
+go get me this."
+In other words,
+we make a *request*
+from the browser.
+This request starts a large chain
+of events
+from your browser
+to the website at that URL
+so that the resource
+from the site
+can get to your eyeballs.
+
+What's in this chain of events?
+*Loads of things are there!*
+We'll gloss over many of the layers
+in this discussion
+because I'm guessing you aren't planning
+to get down to the level
+of how electrical signals work
+in networking cables.
+Instead,
+let's focus
+on two primary parts
+of the chain
+for now: **DNS** and **HTTP**.
+
+### Names Names Names
+
+A URL represents a resource
+that you want
+from the internet.
+How does the internet know
+where it comes from?
+That's where DNS comes in.
+DNS stands for
+{{< extlink "https://en.wikipedia.org/wiki/Domain_Name_System" "Domain Name System" >}}.
+The important word there is "Name."
+Let's return to the address analogy.
+
+In a postal address
+(at least from a US perspective),
+there is the street, city, and state.
+We might write it like:
+
+```text
+123 Main St., Springfield, IL
+```
+
+This address goes from most narrow to most broad.
+123 Main St. is in the city
+of Springfield
+in the state of Illinois (IL).
+
+Likewise,
+a URL fits into a similar format.
+
+```text
+www.example.com
+```
+
+The terminology is different,
+but the concept of going
+from narrow to broad
+is the same.
+Each piece between periods is a type
+of *domain*.
+Let's look at them
+in reverse order.
+
+* `com` is considered a {{< extlink "https://en.wikipedia.org/wiki/Top-level_domain" "Top Level Domain" >}}, TLD.
+ TLDs are carefully managed by a special group
+ called {{< extlink "https://www.icann.org/" "ICANN" >}}.
+* `example` is the domain name.
+ This is the primary identity
+ of a service on the internet
+ as it is the specific identifier
+ which a user would likely recognize.
+* `www` is considered the *subdomain*
+ of a domain.
+ A domain might have many of these
+ like `www`, `m`, `mail`, `wiki`
+ or whatever a domain owner might want to name them.
+ Subdomains can also be more than one level deep
+ so `a.b.example.com` is valid,
+ and `a` is a subdomain of `b.example.com`
+ and `b` is a subdomain of `example.com`.
+
+Domain names are *not* how computers communicate.
+The domain name is something "friendly"
+for a human.
+Networking systems are designed to work
+with numbers
+so those domain names must be translated
+into something the networking system can use.
+To do this,
+the internet uses a system
+of DNS servers
+to act as the translation layer
+between domain names
+and the numbers that computer networks use.
+A server is a special purpose computer
+designed to provide services
+for other devices called clients.
+
+Maybe you've seen these networking numbers.
+The numbers are called IP addresses,
+short for {{< extlink "https://en.wikipedia.org/wiki/Internet_Protocol" "Internet Protocol" >}} addresses.
+Common examples would include:
+
+* `127.0.0.1` as the address that your computer has
+ *for itself*
+ on its internal network.
+* `192.168.0.1` as a default address
+ that a home router might use.
+
+The IP address examples above are special
+because those addresses are in specially designated {{< extlink "https://en.wikipedia.org/wiki/Subnetwork" "subnetworks" >}},
+but we'll set that tangent aside.
+You can delve deeper
+on that topic
+on your own
+if you would like.
+
+Private networks have IP addresses
+like the two examples I listed above.
+Machines on public networks also have IP addresses.
+For instance, `172.253.115.105` is an IP address
+for `www.google.com`
+at the time of this writing.
+
+If you'd like to figure out the IP address
+of a domain name,
+you can install a popular tool named `dig`.
+I found Google's IP address by running this command:
+
+```bash
+dig www.google.com
+```
+
+The system takes domain names
+and keeps a distributed routing table
+of names to IP addresses
+across the collection
+of DNS servers.
+**Wait, what?**
+
+DNS servers stack up into a gigantic hierarchy.
+When your browser makes a request,
+it asks the closest DNS server
+to your machine
+for the IP address
+of the domain name you requested.
+The DNS server keeps a lookup table
+of domain names to IP addresses
+for a period of time.
+If the domain name isn't in the table,
+it can ask another DNS server
+in a chain
+that will continue to look
+for the domain's IP address.
+This leads to a couple of outcomes:
+
+* If none of the servers can find the domain,
+ the browser gives up
+ and shows you a message like
+ "Hmm. We’re having trouble finding that site."
+ (from Firefox's Server Not Found page).
+* If the browser gets the IP address
+ from the DNS server,
+ it can proceed with the request.
+
+The hierarchy is gigantic,
+but it is wide, not deep.
+In other words,
+there are many machines
+that participate in DNS (like your home router),
+but the number of links in the chain
+to make a request from your computer up
+to the root servers in the system
+is relatively small.
+
+This is simplified
+to exclude some
+of the warty corners
+of DNS.
+The Wikipedia page
+that I linked at the start
+of this section covers DNS
+in much greater detail
+if you're interested
+in learning more.
+
+### What Are We Sending?
+
+The other vital piece that we need to explore is HTTP,
+or the {{< extlink "https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol" "Hypertext Transfer Protocol" >}}.
+This part of internet communication describes
+how content transfers
+between browsers
+and servers,
+or,
+more generally,
+between any computers
+that use the protocol.
+
+The protocol uses a standard format
+and a set of commands
+to communicate.
+A few of the common commands are:
+
+* `GET` - Fetch an existing resource
+* `POST` - Create or update a resource
+* `DELETE` - Delete a resource
+* `PUT` - Update a resource
+
+An HTTP request is like sending a text file over the network.
+If you visit my website
+at `https://www.mattlayman.com/about/`,
+your browser will send a request like:
+
+```http
+GET /about/ HTTP/1.1
+Host: www.mattlayman.com
+Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
+```
+
+There are other parts that I've omitted,
+but this gets us started.
+The top line provides the command,
+the path to a particular resource on the site
+(i.e., `/about/`),
+and a version of the protocol to use.
+
+After the first line are a list of *headers*.
+Headers are extra data
+that tell the server more about the request.
+The `Host` header is required
+because it names the website to retrieve
+(more than one website can exist on the same IP address),
+but any other header is optional.
+
+In the example,
+I also showed the `Accept` header.
+This header tells the server
+what kind of content the browser can receive
+as a response.
+There are other headers
+that can tell a server what else it should "know."
+These headers can:
+
+* Indicate what kind of browser is making the request
+ (this is the `User-Agent` header).
+* Tell when the resource was requested previously
+ to determine if a new version should be returned
+ (the `Last-Modified` header).
+* Declare that the browser can receive compressed data
+ which it can decompress after receiving
+ to save on bandwidth
+ (the `Accept-Encoding` header).
+
+Most of the headers are handled automatically
+by browsers and servers,
+but we will see instances
+where we want to use these headers ourselves
+so it's good to know they exist.
+
+## Serving A Response
+
+It's time to discuss Django!
+We now have a rough idea
+of what browsers do.
+A browser sends an HTTP request
+to a URL
+which is resolved
+by the DNS system.
+That request arrives at a server
+that is connected to the IP address
+of the domain name.
+Django lives on such a server
+and is responsible
+for answering requests
+with an HTTP *response*.
+
+The response is what the browser user wanted.
+Responses can be images, web pages, videos,
+or whatever formats a browser can handle.
+
+Before Django can handle a request,
+there is one more layer
+to traverse:
+the Python web server.
+
+### Where HTTP Meets Python
+
+A web server is the software
+on a machine designed
+to handle the incoming HTTP requests.
+Sometimes this terminology can be confusing
+because people may also apply the name "web server"
+to an entire *machine*
+that is serving web traffic.
+In this instance,
+I'm referring
+to the actual program listening and responding
+to web requests.
+
+A Python web framework
+like Django
+runs with a web server.
+The web server's role is to translate the raw HTTP request
+into a format
+that the framework understands.
+In the Python world,
+there is a specific format used
+so that any web server
+can talk to any Python web framework.
+That format is the {{< extlink "https://wsgi.readthedocs.io/en/latest/what.html" "Web Server Gateway Interface" >}},
+or WSGI
+(which is often pronounced "wiz-gee").
+
+{{< web >}}
+{{< figure src="/img/2020/wsgi.jpg" caption="Web Server Gateway Interface" >}}
+{{< /web >}}
+
+WSGI enables common web servers
+like
+{{< extlink "https://gunicorn.org/" "Gunicorn" >}},
+{{< extlink "https://uwsgi-docs.readthedocs.io/en/latest/" "uWSGI" >}},
+or {{< extlink "https://modwsgi.readthedocs.io/en/develop/" "mod_wsgi" >}}
+to communicate
+with common Python web frameworks
+like Django,
+{{< extlink "https://palletsprojects.com/p/flask/" "Flask" >}},
+or {{< extlink "https://trypyramid.com/" "Pyramid" >}}.
+If you really want to nerd out,
+you can explore all the details
+of that format
+in {{< extlink "https://www.python.org/dev/peps/pep-3333/" "PEP 3333" >}}.
+
+### Django's Job
+
+Once the web server sends a request,
+Django needs to return a *response*.
+Your role as a Django developer is
+to define the resources
+that will be available
+from the server.
+That means you must:
+
+* Describe the set of URLs
+ that Django will react to.
+* Write the code
+ that powers those URLs
+ and returns the response.
+
+There is a ton
+to unpack in those two statements
+so we will explore individual topics
+{{< web >}}
+in future articles.
+{{< /web >}}
+{{< book >}}
+in future chapters.
+{{< /book >}}
+By now,
+I hope you have an idea
+of how a request gets
+from your browser
+to a machine running Django.
+
+{{< web >}}
+{{< figure src="/img/2020/request-response.jpg" caption="Life of a browser request" >}}
+{{< /web >}}
+
+{{< web >}}
+This article is relatively free
+{{< /web >}}
+{{< book >}}
+This chapter is relatively free
+{{< /book >}}
+of code examples,
+and for good reason.
+There are already enough concepts
+to wrestle with
+and I didn't want to add code complexity
+on top of it.
+Writing that code will be the focus
+{{< web >}}
+of this article series
+{{< /web >}}
+{{< book >}}
+of this book
+{{< /book >}}
+so we can answer questions like:
+
+* How do we build web pages
+ and give everything a common look and feel?
+* How can users interact
+ with an application
+ and send data
+ that the app can react to?
+* How does Django store and retrieve data
+ to make sites dynamic?
+* Who can access the application
+ and how is that access controlled?
+* What security do we need to add
+ to ensure that our users' information is safe and private?
+
+Django has answers for all these things
+and way more.
+The Django philosophy is to include all the required pieces
+to make a full web application
+for the internet.
+This "batteries-included" philosophy is what makes Django so powerful.
+The same philosophy can also make Django seem overwhelming.
+{{< web >}}
+My goal in this series is to introduce piece after piece
+{{< /web >}}
+{{< book >}}
+My goal in this book is to introduce piece after piece
+{{< /book >}}
+to build your understanding of Django
+so you can get productive and get going
+on your own web application.
+
+{{< web >}}
+In the next article,
+our focus is going to be
+on those URLs
+that our application
+will respond to.
+We will see:
+
+* how to declare the URLs.
+* how to group sets of related URLs.
+* how to extract information from URLs
+ that can be used by the code that returns responses.
+
+If you'd like to follow along
+with the series,
+please feel free to sign up
+for my newsletter
+where I announce all of my new content.
+If you have other questions,
+you can reach me online
+on Twitter
+where I am
+{{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
+{{< /web >}}
+
+Finally,
+there is one more bonus topic...
+
+## Getting Django Set Up
+
+{{< web >}}
+In the series,
+{{< /web >}}
+{{< book >}}
+In the book,
+{{< /book >}}
+we'll be looking at plenty of code examples,
+but we won't be setting up Django from scratch each time.
+The following setup instructions will help you get started
+with each future example.
+
+> The goal of this section is not meant to be an authoritative description
+of how to set up your Python environment.
+I am assuming that you have some knowledge of how to run Python code.
+If you need a more descriptive guide,
+I'd suggest Michael Kennedy's
+[Installing Python 3](https://training.talkpython.fm/installing-python) article
+and Real Python's
+[primer on virtual environments](https://realpython.com/python-virtual-environments-a-primer/).
+These article go into the discussion of setup far more
+than I'm doing justice here.
+
+We're going to use a terminal to run commands.
+Windows, macOS, and Linux are all a bit different.
+I'm showing macOS here
+because that's what I run.
+The dollar sign (`$`) is the traditional starting character
+for a bash terminal
+so when I list commands,
+don't type that character.
+I'll try to give pointers
+and highlight differences when I can.
+
+We need a place to put our work.
+{{< web >}}
+Since this series is called "Understand Django,"
+{{< /web >}}
+{{< book >}}
+Since this book is called "Understand Django,"
+{{< /book >}}
+I'm going to use that name.
+You can name your project differently if you prefer.
+
+```bash
+$ mkdir understand-django
+$ cd understand-django
+```
+
+Next,
+we install Django
+into a virtual environment
+so we keep our project dependencies separate
+from the rest
+of the installed Python packages
+on our machine.
+Having this separation
+from other installed packages
+is a good way to prevent conflicts
+with other Python projects
+that you may be running
+on your computer.
+
+```bash
+$ python3 -m venv venv
+$ source venv/bin/activate
+```
+
+This may change your terminal prompt
+so that it will now start with `(venv)`
+to tell you that the virtual environment is in use.
+Other operating systems activate the virtual environment differently.
+Check the {{< extlink "https://docs.python.org/3/library/venv.html" "venv module documentation" >}}
+for more information
+on your operating system.
+
+Now you can install Django,
+and the Django framework code will be added
+to the virtual environment.
+
+```bash
+(venv) $ pip install Django
+```
+
+Django includes some tools
+which we can use
+to get a project started quickly.
+We'll run a single command
+to get it going.
+
+```bash
+(venv) $ django-admin startproject project .
+```
+
+This commands says
+"start a project
+*named* 'project'
+in the current directory (`.`)."
+The choice of "project" as the name is intentional.
+`startproject` will create a directory
+named `project` that will contain various files
+that you'll use to configure your entire web app.
+You can name your project whatever you like,
+but I find that using the generic name makes my life easier
+as I switch between different Django web apps.
+I always know where my project related files reside.
+After that command is finished,
+you should have some files
+and a layout that looks like:
+
+```bash
+(venv) $ ls
+manage.py project venv
+```
+
+Notice that,
+in addition to the `project` directory,
+Django created a `manage.py` file.
+This file is a script that will help you interact
+with Django.
+You'll learn a lot more about `manage.py`
+as we get farther along.
+To check if the basics are working,
+try:
+
+```bash
+(venv) $ python manage.py runserver
+...
+Starting development server at http://127.0.0.1:8000/
+Quit the server with CONTROL-C.
+```
+
+When you start the web server,
+you will likely see a message
+like:
+
+```text
+You have ## unapplied migration(s).
+Your project may not work properly
+until you apply the migrations for app(s):
+
+```
+
+We'll explore the migrations topic later,
+so don't worry about that message for now.
+
+If you copy and paste that URL
+(i.e., `http://127.0.0.1:8000/`)
+into your browser,
+you should see a welcoming start page!
+Also,
+if you look back at your terminal,
+you'll find `"GET / HTTP/1.1"`.
+This message is showing that Django responded
+to an HTTP request.
+Neat!
+
+The other thing that we need is an "app."
+This is (perhaps confusingly) the name
+of a Django component
+in a project.
+What you need to remember is
+that a Django project *contains* one or more apps.
+Apps will hold most
+of your code
+that you need to write
+when working with Django.
+
+After you have quit the server,
+you can create an app to work with:
+
+```bash
+(venv) $ python manage.py startapp application
+```
+
+This will generate another set of files
+that follow the standard structure
+of a Django application component
+inside a directory called `application`.
+This example uses a boring name,
+but,
+unlike `project`,
+you should pick a name
+that makes sense for your web app
+(e.g., `movies` would be a good name
+for a web app that is about movies).
+All of these files will be discussed
+in detail in a future topic.
+
+Finally,
+we must hook that app
+into Django's project settings.
+The project settings allow you to configure Django
+to suit your needs.
+Open up `project/settings.py`,
+find `INSTALLED_APPS`
+and append to the list
+so it looks like:
+
+```python
+INSTALLED_APPS = [
+ 'django.contrib.admin',
+ 'django.contrib.auth',
+ 'django.contrib.contenttypes',
+ 'django.contrib.sessions',
+ 'django.contrib.messages',
+ 'django.contrib.staticfiles',
+ 'application',
+]
+```
+
+That's as far as we need to go
+to get started
+with our code examples
+{{< web >}}
+in the next article.
+{{< /web >}}
+{{< book >}}
+in the next chapter.
+{{< /book >}}
+`application` will be our reference app.
+The code in future topics is not a tutorial,
+but I will use `application` on occasion
+to orient you to where you would find files
+in your own Django web app.
+We have a Django project
+that can run locally
+for testing
+and is configured
+with its first app.
+{{< web >}}
+See you soon
+to talk about making URLs and resources!
+{{< /web >}}
diff --git a/content/understand-django/2020-01-22-urls-lead-way.pt.md b/content/understand-django/2020-01-22-urls-lead-way.pt.md
new file mode 100644
index 00000000..8c7b02d8
--- /dev/null
+++ b/content/understand-django/2020-01-22-urls-lead-way.pt.md
@@ -0,0 +1,878 @@
+---
+title: "URLs Lead The Way"
+description: >-
+ How does a Django site know
+ where to send requests?
+ You have to tell it!
+ In this next article
+ in the Understand Django series,
+ we look at URLs
+ and how to let your users get
+ to the right place.
+image: img/django.png
+type: post
+categories:
+ - Python
+ - Django
+tags:
+ - Python
+ - Django
+
+---
+
+{{< web >}}
+In the last article
+in the
+[Understand Django]({{< ref "/understand-django/_index.md" >}})
+series,
+we saw how a user's browser request goes
+from their browser
+to Django's "front door."
+{{< /web >}}
+Now it's time to look
+{{< web >}}
+at how Django processes those requests.
+{{< /web >}}
+{{< book >}}
+at how Django processes a user's browser requests.
+{{< /book >}}
+
+An HTTP request coming
+from a browser
+includes a URL describing which resource Django should produce.
+Since URLs can come
+in many forms,
+we must instruct Django
+on the kinds
+of URLs
+that our web application can handle.
+This is what the *URL configuration* is for.
+In the Django documentation,
+the URL configuration is called a URLconf,
+for short.
+
+Where is the URLconf?
+The URLconf is at the module path set
+by the `ROOT_URLCONF` setting
+in your project's settings file.
+If you ran the `startproject` command,
+then that setting will be named
+like `project.urls`
+where "project" is the name given
+as an argument
+to the command.
+In other words,
+the URLconf is placed
+in `project/urls.py`,
+right next to the `settings.py` file.
+
+That explains where the file resides,
+but it doesn't tell us much
+about how it works.
+Let's dig in more.
+
+{{< understand-django-series "urls" >}}
+
+## URLconf In Action
+
+Try to think
+of the URL configuration
+as a list
+of URL paths
+that Django will attempt to match
+from top to bottom.
+When Django finds a matching path,
+the HTTP request will route
+to a chunk of Python code
+that is associated with that path.
+That "chunk of Python code" is called a *view*
+which we will explore more
+in a bit.
+For the moment,
+trust that views know how
+to handle HTTP requests.
+
+We can use an example URLconf
+to bring this to life.
+
+```python
+# project/urls.py
+from django.urls import path
+
+from application import views
+
+urlpatterns = [
+ path("", views.home),
+ path("about/", views.about),
+ path("contact/", views.contact),
+ path("terms/", views.terms),
+]
+```
+
+What's here matches well
+with what I described above:
+a list of URL paths
+that Django will try to match
+from top to bottom.
+The key aspect of this list is the name `urlpatterns`.
+Django will treat the list
+in a `urlpatterns` variable
+as the URLconf.
+
+The order of this list is also important
+because Django will stop scanning the list
+as soon as it encounters a match.
+The example doesn't show any conflict
+between paths,
+but it's possible to create two different `path` entries
+that can match the same URL
+that a user submits.
+I'll show an example
+of how that can happen
+after we see another aspect
+of paths.
+
+We can work through an example
+to see how this would work
+for `www.example.com`.
+When considering a URL
+in a URLconf,
+Django ignores the scheme (`https://`),
+the domain (`www.example.com`),
+and the leading slash
+for matching.
+Everything else is what the URLconf will match against.
+
+* A request to `https://www.example.com/about/` will look
+ like `"about/"`
+ to the pattern matching process
+ and match the second `path`.
+ That request would route
+ to the `views.about` view.
+* A request to `https://www.example.com/` will look
+ like `""`
+ to the pattern matching process
+ and match the first `path`.
+ That request would route
+ to the `views.home` view.
+
+> Aside:
+You might notice
+that Django URLs end
+with a slash character.
+This behavior is because
+of a Django {{< extlink "https://docs.djangoproject.com/en/4.1/misc/design-philosophies/#definitive-urls" "design philosophy" >}} choice.
+In fact,
+if you attempt to reach a URL
+like `https://www.example.com/about`,
+Django will redirect the request
+to the same URL
+with the slash appended
+because of the `APPEND_SLASH`
+{{< extlink "https://docs.djangoproject.com/en/4.1/ref/settings/#append-slash" "default setting" >}}.
+
+## The `path` Before Us
+
+The string part of `path`
+(e.g., `"about/"`) is called the *route*.
+A route can be a plain string
+as you've seen,
+but it can include other special structures
+with a feature called *converters*.
+When you use a converter,
+you can extract information out
+of a URL
+that a view can use later.
+Consider a path like this:
+
+```python
+ path(
+ "blog///",
+ views.blog_post
+ ),
+```
+
+The two converters in this path are:
+
+* ``
+* ``
+
+The use of angle brackets
+and some {{< extlink "https://docs.djangoproject.com/en/4.1/topics/http/urls/#path-converters" "reserved names" >}}
+cause Django to perform extra parsing
+on a URL.
+Each converter has some expected rules to follow.
+
+* The `int` converter must match an integer.
+* The `slug` converter must match a slug.
+ Slug is a bit of newspaper lingo
+ that appears in Django
+ because Django started
+ as a project
+ out of a newspaper in Kansas.
+ A slug is a string that can include characters, numbers, dashes, and underscores.
+
+Given those converter definitions,
+let's compare against some URLs!
+
+* `https://www.example.com/blog/2020/urls-lead-way/` - MATCH!
+* `https://www.example.com/blog/twenty-twenty/urls-lead-way/` - NOPE.
+* `https://www.example.com/blog/0/life-in-rome/` - MATCH!
+ Uh, maybe not what we wanted though.
+ Let's look at that soon.
+
+Now we can revisit our ordering problem
+from earlier.
+Consider these two paths
+in different orders:
+
+```python
+ path(
+ "blog//",
+ views.blog_by_year
+ ),
+ path(
+ "blog/2020/",
+ views.blog_for_twenty_twenty
+ ),
+
+# vs.
+
+ path(
+ "blog/2020/",
+ views.blog_for_twenty_twenty
+ ),
+ path(
+ "blog//",
+ views.blog_by_year
+ ),
+```
+
+In the first ordering,
+the converter will match any integer following `blog/`,
+including `https://www.example.com/blog/2020/`.
+That means that the first ordering will never call the `blog_for_twenty_twenty` view
+because Django matches `path` entries in order.
+
+Conversely,
+in the second ordering,
+`blog/2020/` will route to `blog_for_twenty_twenty` properly
+because it is matched first.
+That means there's a lesson
+to remember here:
+
+{{< web >}}
+> When including `path` entries that match
+ on ranges of values
+ with converters (like the years example above),
+ be sure to put them **after** the more specific entries.
+{{< /web >}}
+{{< book >}}
+When including `path` entries that match
+ on ranges of values
+ with converters (like the years example above),
+ be sure to put them **after** the more specific entries.
+{{< /book >}}
+
+## An Abbreviated View Of Views
+
+What do converters do with this extra data?
+That's hard to explain
+without touching on views.
+{{< web >}}
+The next article will cover views
+{{< /web >}}
+{{< book >}}
+The next chapter will cover views
+{{< /book >}}
+in far more depth,
+but here's a primer.
+
+A view is code
+that takes a request
+and returns a response.
+Using Python's optional type hinting,
+here's an example
+that will send a `Hello World` response.
+
+```python
+from django.http import (
+ HttpRequest,
+ HttpResponse
+)
+
+def some_view(
+ request: HttpRequest
+) -> HttpResponse:
+ return HttpResponse('Hello World')
+```
+
+The `HttpRequest` is Django's translated format
+of an HTTP request
+wrapped up in a convenient container class.
+Likewise, `HttpResponse` is what we can use
+so that Django will translate our response data
+into a properly formatted HTTP response
+that will be sent back to the user's browser.
+
+Now we can look
+at one of the converters again.
+
+```python
+ path(
+ "blog//",
+ views.blog_by_year
+ ),
+```
+
+With this converter in place
+in the route,
+what would `blog_by_year` look like?
+
+```python
+# application/views.py
+from django.http import HttpResponse
+
+def blog_by_year(request, year):
+ # ... some code to handle the year
+ data = 'Some data set by code above'
+ return HttpResponse(data)
+```
+
+Django begins to reveal some nice qualities here!
+The converter did a bunch
+of tedious work
+for us.
+The `year` argument set
+by Django
+will already be an integer
+because Django did the string parsing
+and conversion.
+
+If someone submits `/blog/not_a_number/`,
+Django will return a Not Found response
+because `not_a_number` can't be an integer.
+The benefit of this
+is that we don't have to put extra checking logic
+in `blog_by_year`
+to handle the weird case where `year` doesn't look like a number.
+That kind of feature is a real time saver!
+It keeps your code cleaner
+*and* makes handling more precise.
+
+What about that other strange example
+that we saw earlier
+of `/blog/0/life-in-rome/`?
+That would match our pattern
+from the earlier section,
+but let's assume we want to match a four digit year.
+How can we do that?
+We can use regular expressions.
+
+## Regular Expression Paths
+
+Regular expressions are a programming feature
+often likened to a chainsaw:
+*they are incredibly powerful,
+but you can cut off your foot
+if you're not careful.*
+
+Regular expressions can express complex patterns
+of characters
+in a concise way.
+This conciseness often gives regular expressions a bad reputation
+of being difficult to understand.
+When used carefully, though,
+they can be highly effective.
+
+A regular expression
+(which is often abbreviated to "regex")
+matches complex patterns
+in strings.
+This sounds exactly like our blog year problem!
+In our problem,
+we want to match a four digit integer only.
+Let's look at a solution
+that Django can handle
+and then break down what it means.
+
+As a reminder,
+this solution will match some URL path
+like `blog/2020/urls-lead-way/`.
+
+Note, we use the `re_path()` function for
+regular expression matching here, instead of `path()`.
+
+```python
+re_path(
+ r"^blog/(?P[0-9]{4})/(?P[\w-]+)/$",
+ views.blog_post
+),
+```
+
+This crazy string behaves exactly like our earlier example
+**except** that it is more precise
+about only allowing four digit years.
+The crazy string also has a name.
+It is called a *regex pattern*.
+When the Django code runs,
+it will test URL paths against the rules
+that are defined in this pattern.
+
+To see how it works,
+we have to know what the parts of the pattern mean.
+We can explain this pattern one chunk
+at a time.
+
+* The string itself starts with `r"`
+ because it is a raw string in Python.
+ This is used because regular expressions use `\` extensively.
+ Without a raw string,
+ a developer would have to escape the backslash repeatedly
+ by using `\\`.
+* The caret, `^`, means "the pattern must *start* here."
+ Because of the caret,
+ a path that starts like `myblog/...` will not work.
+* `blog/` is a literal interpretation.
+ Those characters must match exactly.
+* The portion inside parentheses `(?P[0-9]{4})` is a *capture group*.
+ The `?P` is the name to associate
+ with the capture group and is similar
+ to the right side of the colon
+ in a converter like ``.
+ The name allows Django
+ to pass on the content
+ in an argument called `year`
+ to the view.
+ The other part of the capture group, `[0-9]{4}`,
+ is what the pattern is actually matching.
+ `[0-9]` is a *character class*
+ which means "match any number from 0 through 9."
+ The `{4}` means that it must match **exactly** four times.
+ This is the specificity that `re_path` gives
+ that the `int` converter could not!
+* The slash, `/`, between capture groups is another literal character to match.
+* The second capture group, `(?P[\w-]+)`, will put whatever it matches
+ into an argument named `slug`.
+ The character class of `[\w-]` contains two types
+ of characters. `\w` means any word character
+ that you might have in a natural language
+ and digits and underscores.
+ The other type of character is a literal dash, `-`, character.
+ Finally, the plus, `+`, character means
+ that the character class must match 1 or more times.
+* The last slash is also a literal character match.
+* To complete the pattern,
+ the dollar sign, `$`, acts like the opposite
+ of the caret and means
+ "the pattern must *end* here."
+ Thus, `blog/2020/some-slug/another-slug/` will not match.
+
+Note that you cannot mix the `path` style and `re_path` style strings.
+The example above had to describe the slug as a regular expression
+instead of using the slug converter (i.e., ``).
+
+Congratulations!
+This is definitely the hardest section
+{{< web >}}
+of this article.
+{{< /web >}}
+{{< book >}}
+of this chapter.
+{{< /book >}}
+If you understood what we did
+with `re_path`,
+the rest of this should feel very comfortable.
+If not,
+*please don't fret about it!*
+If you want to know more about regular expressions,
+know that everything I described
+in the pattern
+is *not* Django specific.
+Instead,
+this is Python's built-in behavior.
+You can learn more
+about regular expressions
+from Python's {{< extlink "https://docs.python.org/3/howto/regex.html" "Regular Expression HOWTO" >}}.
+
+Knowing that this power
+with `re_path`
+is there may help you later
+on your Django journey,
+even if you don't need it now.
+
+## Grouping Related URLs
+
+Up to this point,
+we've looked at individual routes
+that you can map
+in a URLconf.
+What can we do
+when a related group
+of views
+should share a common path?
+Why would we want to do this?
+
+Let's imagine you're building an educational project.
+In your project,
+you have schools, students, and other education related concepts.
+You *could* do something like:
+
+```python
+# project/urls.py
+from django.urls import path
+
+from schools import (
+ views as schools_views,
+)
+from students import (
+ views as students_views,
+)
+
+urlpatterns = [
+ path(
+ "schools/", schools_views.index
+ ),
+ path(
+ "schools//",
+ schools_views.school_detail,
+ ),
+ path(
+ "students/",
+ students_views.index,
+ ),
+ path(
+ "students//",
+ students_views.student_detail,
+ ),
+]
+```
+
+This approach would work fine,
+but it forces the root URLconf
+to know about all the views defined
+in each app, `schools` and `students`.
+Instead,
+we can use `include`
+to handle this better.
+
+```python
+# project/urls.py
+from django.urls import include, path
+
+urlpatterns = [
+ path(
+ "schools/",
+ include("schools.urls"),
+ ),
+ path(
+ "students/",
+ include("students.urls"),
+ ),
+]
+```
+
+Then,
+in each application,
+we would have something like:
+
+```python
+# schools/urls.py
+from django.urls import path
+
+from schools import views
+
+urlpatterns = [
+ path("", views.index),
+ path(
+ "/",
+ views.school_detail
+ ),
+]
+```
+
+The use of `include` gives each Django app autonomy
+in what views it needs to define.
+The project can be blissfully "ignorant"
+of what the application is doing.
+
+Additionally,
+the repetition of `schools/` or `students/` is removed
+from the first example.
+As Django processes a route,
+it will match
+on the first portion
+of the route
+and pass the *remainder*
+onto the URLconf
+that is defined in the individual app.
+In this way,
+URL configurations can form a tree
+where the root URLconf is where all requests start,
+but individual applications can handle the details
+as a request is routed to the proper app.
+
+## Naming URLs
+
+We've looked at the main ways
+that URLs get defined
+with `path`, `re_path`, and `include`.
+There is another aspect to consider.
+How can we refer to URLs
+in other places in the code?
+Consider this (rather silly) view:
+
+```python
+# application/views.py
+from django.http import (
+ HttpResponseRedirect
+)
+
+def old_blog_categories(request):
+ return HttpResponseRedirect(
+ "/blog/categories/"
+ )
+```
+
+A redirect is when a user tries to visit a page
+and is sent somewhere else
+by the browser.
+There are much better ways to handle redirects
+than this example shows,
+but this view illustrates a different point.
+What would happen if you want to restructure the project
+so that blog categories moved
+from `/blog/categories/`
+to `/marketing/blog/categories/`?
+In the current form,
+we would have to fix this view
+and any other view
+that referenced the route directly.
+
+What a waste of time!
+Django gives us tools to give paths names
+that are independent
+from the explicit route.
+We do this with the `name` keyword argument to `path`.
+
+```python
+# project/urls.py
+from django.urls import path
+
+from blog import views
+
+urlpatterns = [
+ ...
+ path(
+ "/marketing/blog/categories/",
+ views.categories,
+ name="blog_categories"
+ ),
+ ...
+]
+```
+
+This gives us `blog_categories`
+as an independent name
+from the route
+of `/marketing/blog/categories/`.
+To use that name,
+we need `reverse`
+as its counterpart.
+Our modified view looks like:
+
+```python
+# application/views.py
+from django.http import (
+ HttpResponseRedirect
+)
+from django.urls import reverse
+
+def old_blog_categories(request):
+ return HttpResponseRedirect(
+ reverse("blog_categories")
+ )
+```
+
+The job of `reverse`
+is to look up any path name
+and return its route equivalent.
+That means that:
+
+```python
+reverse("blog_categories") == "/marketing/blog/categories/"
+```
+
+At least until you want to change it again. 😁
+
+## When Names Collide
+
+What happens
+if you have multiple URLs
+that you want to give the same `name`?
+For instance,
+`index` or `detail` are common names
+that you may want to apply.
+We can turn to
+{{< extlink "https://www.python.org/dev/peps/pep-0020/" "The Zen of Python" >}}
+for advice.
+
+> The Zen of Python, by Tim Peters
+>
+> Beautiful is better than ugly.
+>
+> ...
+>
+> **Namespaces are one honking great idea -- let's do more of those!**
+
+Namespaces might be new to you
+if you haven't been programming long.
+They are a *shared space for names*.
+Maybe that's clear,
+but I recall struggling
+with the concept
+when I first began to write software.
+
+To make an analogy
+to something in the real world,
+let's use trusty buckets.
+Imagine you have two red balls
+and two blue balls.
+Put one ball of each color
+in each of the two buckets labeled "A" and "B."
+If I wanted a specific blue ball,
+I can't say "please give me the blue ball"
+because that would be ambiguous.
+Instead,
+to get a specific ball,
+I would need to say "please give me the blue ball in bucket B."
+In this scenario,
+the bucket is the namespace.
+
+The example that we used for schools and students
+can help illustrate this idea
+in code.
+Both apps had an `index` view
+to represent the root
+of the respective portions of the project
+(i.e., `schools/` and `students/`).
+If we wanted to refer
+to those views,
+we'd try to pick the easiest choice
+of `index`.
+Unfortunately,
+if you pick `index`,
+then Django can't tell which one is the right view
+for `index`.
+The name is ambiguous.
+
+One solution is to create your own namespace
+by prefixing `name`
+with something common
+like `schools_`.
+The trouble with that approach is that the URLconf repeats itself.
+
+```python
+# schools/urls.py
+from django.urls import path
+
+from schools import views
+
+urlpatterns = [
+ path(
+ "",
+ views.index,
+ name="schools_index"
+ ),
+ path(
+ "/",
+ views.school_detail,
+ name="schools_detail"
+ ),
+]
+```
+
+Django provides an alternative
+that will let you keep a shorter name.
+
+```python
+# schools/urls.py
+from django.urls import path
+
+from schools import views
+
+app_name = "schools"
+urlpatterns = [
+ path("", views.index, name="index"),
+ path(
+ "/",
+ views.school_detail,
+ name="detail"
+ ),
+]
+```
+
+By adding `app_name`,
+we signal to Django
+that these views are in a namespace.
+Now when we want to get a URL,
+we use the namespace name
+and the URL name
+and join them
+with a colon.
+
+```python
+reverse("schools:index") == "/schools/"
+```
+
+This is another convenience
+that Django gives
+to make our application development experience easier.
+
+That brings us to a close
+on the subject of URLs.
+By now,
+we've seen how to:
+
+* Make a URL configuration
+ by making a module with a list of `urlpatterns`.
+* Create URLs with `path` and `re_path`.
+* Use converters to extract information for views.
+* Use regular expressions to express more complex URL data.
+* Group related URLs together with `include`.
+* Refer to a URL by its `name`.
+* Put related names together in a namespace.
+
+{{< web >}}
+In the next article,
+we'll dig into views.
+This article only gave the briefest definition
+{{< /web >}}
+{{< book >}}
+In the next chapter,
+we'll dig into views.
+This chapter only gave the briefest definition
+{{< /book >}}
+to what a view is.
+Django gives us very rich options
+when working with views.
+We're going to explore:
+
+* View functions
+* View classes
+* Some built-in supporting views
+* Decorators that supercharge views.
+
+{{< web >}}
+If you'd like to follow along
+with the series,
+please feel free to sign up
+for my newsletter
+where I announce all of my new content.
+If you have other questions,
+you can reach me online
+on Twitter
+where I am
+{{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
+{{< /web >}}
+
diff --git a/content/understand-django/2020-03-03-views-on-views.pt.md b/content/understand-django/2020-03-03-views-on-views.pt.md
new file mode 100644
index 00000000..0d21eb90
--- /dev/null
+++ b/content/understand-django/2020-03-03-views-on-views.pt.md
@@ -0,0 +1,963 @@
+---
+title: "Views On Views"
+description: >-
+ Django URLs expect to send a response
+ back to a user.
+ Where does that response come from?
+ A Django view!
+ This article looks
+ into the fundamentals
+ of views
+ and how to use them
+ in your project.
+image: img/django.png
+type: post
+categories:
+ - Python
+ - Django
+tags:
+ - Python
+ - Django
+ - views
+
+---
+
+{{< web >}}
+In the previous
+[Understand Django]({{< ref "/understand-django/_index.md" >}})
+article,
+I covered URLs
+and the variety
+of tools
+that Django gives us
+to describe the outside interface
+to the internet
+for your project.
+In this article,
+{{< /web >}}
+{{< book >}}
+Now that we have a grasp
+on URLs in Django,
+{{< /book >}}
+we'll examine the core building block
+that makes those URLs work:
+the Django view.
+
+{{< understand-django-series "views" >}}
+
+## What Is A View?
+
+A view is a chunk of code
+that receives an HTTP request
+and returns an HTTP response.
+Views are where you use Django's core functionality:
+to respond to requests
+made to an application
+on the internet.
+
+You might notice
+that I'm a bit vague
+about "chunk of code."
+That was deliberate.
+The reason is that views come
+in multiple forms.
+To say views are *functions*
+would be part
+of the story.
+Later chapters in that story cover
+how they can also be implemented in *classes*.
+
+Even if I attempted
+to call views *callables,*
+I still would not portray them accurately
+because of the ways
+that certain types of views
+get plugged into a Django app.
+For instance,
+a view based on a class will *produce* a callable
+as we'll see in a later section.
+
+Let's start with functions
+since I think they are the gentlest introduction
+to views.
+
+## Function Views
+
+A function view is precisely that, a function.
+The function takes an `HttpRequest` instance
+as input
+and returns an `HttpResponse`
+(or one of its many subclasses)
+as output.
+
+The classic "Hello World" example
+would look like what is listed below.
+
+```python
+# application/views.py
+from django.http import HttpResponse
+
+def hello_world(request):
+ return HttpResponse('Hello World')
+```
+
+Adding the `hello_world` view
+to a URL configuration
+which we learned about
+{{< web >}}
+in the last article,
+{{< /web >}}
+{{< book >}}
+in the last chapter,
+{{< /book >}}
+you could visit a browser
+at the URL
+and find the text "Hello World"
+on your browser page.
+
+Maybe you don't find that very exciting,
+but I do,
+and I think you should!
+The framework did so much work for us,
+and *our* job is to write a mere couple of lines
+of Python.
+When plugged into a web server
+on the internet,
+your greeting can reach anyone
+with access to the net.
+That's staggering
+and is worth reflecting on.
+
+Django does most of the heavy lifting
+for us.
+The raw HTTP request fits neatly
+into the `HttpRequest` class.
+Our example view doesn't use that information,
+but it's accessible
+if we need it.
+Likewise,
+we're not using much
+of `HttpResponse`.
+Still,
+it's doing all the work
+to ensure it appears
+on a user's browser
+and delivers our message.
+
+To see what we can do with views,
+let's look closely
+at `HttpRequest` and `HttpResponse`
+to get a glimpse
+at what's going on.
+
+## HttpRequest
+
+`HttpRequest` is a Python class.
+Instances of this class represent an HTTP request.
+HTTP is the transfer protocol
+that the internet uses to exchange information.
+A request can be in a variety of formats,
+but a standard request might look like:
+
+```http
+POST /courses/0371addf-88f7-49e4-ac4d-3d50bb39c33a/edit/ HTTP/1.1
+Host: 0.0.0.0:5000
+Accept-Language: en-US,en;q=0.5
+Accept-Encoding: gzip, deflate
+Content-Type: application/x-www-form-urlencoded
+Content-Length: 155
+Origin: http://0.0.0.0:5000
+Connection: keep-alive
+Upgrade-Insecure-Requests: 1
+Pragma: no-cache
+Cache-Control: no-cache
+
+name=Science
+&monday=on
+&tuesday=on
+&wednesday=on
+&thursday=on
+&friday=on
+```
+
+{{< web >}}
+This example is from a side project
+that uses school data.
+I have trimmed some lines out
+of the request so it will fit better
+on the screen,
+and I did some slight reformatting
+to make the content a bit clearer.
+{{< /web >}}
+
+
+When Django receives a request like this,
+it will parse the data
+and store it in an `HttpRequest` instance.
+The request provides convenient access
+to all parts
+of the raw data
+with helpful attributes
+for the most commonly used parameters.
+When considering the example,
+the request would have:
+
+* `method` - This matches the HTTP method of `POST`
+ and can be used to act
+ on the *kind* of request the user sent.
+* `content_type` - This attribute instructs Django
+ on how to handle the data
+ in the request.
+ The example value would be `application/x-www-form-urlencoded `
+ to indicate that this is user-submitted form data.
+* `POST` - For POST requests,
+ Django processes the form data
+ and stores the data
+ into a dictionary-like structure.
+ `request.POST['name']` would be `Science`
+ in our example.
+* `GET` - Anything added to the query string
+ (i.e., the content after a `?` character
+ such as `student=Matt`
+ in `/courses/?student=Matt`) is stored
+ in a dictionary-like attribute as well.
+* `headers` - This is where all the HTTP headers
+ like `Host`, `Accept-Language`,
+ and the others are stored.
+ `headers` is also dictionary-like
+ and can be accessed like `request.headers['Host']`.
+
+Other attributes are available
+to `HttpRequest`,
+but that list will get you far enough to get started.
+Check out
+{{< extlink "https://docs.djangoproject.com/en/4.1/ref/request-response/" "Request and response objects" >}}
+for the other attributes.
+
+I should also note
+that `HttpRequest` instances are a common place
+to attach extra data.
+Django requests pass through many pieces
+in the framework.
+This makes the objects great candidates
+for extra features that you may require.
+For instance,
+if you need user management
+{{< web >}}
+(which we will explore in a future article),
+{{< /web >}}
+{{< book >}}
+(which we will explore in a future chapter),
+{{< /book >}}
+there is code
+that can attach a `request.user` attribute
+to represent a user
+in your system.
+It's *very* handy.
+
+You can think of `HttpRequest` objects
+as the common interface
+for most of the inputs
+that my code uses.
+
+## HttpResponse
+
+The other major interface
+that your views will use
+either directly or indirectly
+is the `HttpResponse` interface.
+
+Your job as a Django user
+is to make your views return
+some kind of `HttpResponse`.
+A response instance will include all the necessary information
+to create a valid HTTP response
+for a user's browser.
+
+Some of the common `HttpResponse` attributes include:
+
+* `status_code` - This is the HTTP status code.
+ Status codes are a set of numbers
+ that HTTP defines
+ to tell a client (e.g., a browser)
+ about the success or failure
+ of a request.
+ `200` is the usual success code.
+ Any number from `400` and up will indicate some error,
+ like `404` when a requested resource is not found.
+* `content` - This is the content
+ that you provide to the user.
+ The response stores this data as bytes.
+ If you supply Python string data,
+ Django will encode it to bytes for you.
+
+```python
+>>> from django.http import HttpResponse
+>>> response = HttpResponse('Hello World')
+>>> response.content
+b'Hello World'
+```
+
+When working with Django views,
+you won't always use `HttpResponse` directly.
+`HttpResponse` has a variety
+of subclasses
+for common uses.
+Let's look at some:
+
+* `HttpResponseRedirect` - You may want to send a user
+ to a different page.
+ Perhaps the user bought something
+ on your site,
+ and you would like them to see a receipt page
+ of their order.
+ This subclass is perfect
+ for that scenario.
+* `HttpResponseNotFound` - This is the subclass used
+ to create a `404 Not Found` response.
+ Django provides some helper functions to return this
+ so you might not use this subclass directly,
+ but it's good to know it's available.
+* `HttpResponseForbidden` - This type of response can be used
+ when you don't want a user
+ to access a part
+ of your website
+ (i.e., HTTP status `403 Forbidden`).
+
+Aside from the subclasses,
+Django has other techniques
+to return `HttpResponse` instances
+without creating one yourself.
+The most common function is `render`.
+
+`render` is a tool
+for working with templates.
+Templates are the topic
+{{< web >}}
+of the next article,
+{{< /web >}}
+{{< book >}}
+of the next chapter,
+{{< /book >}}
+but here is a sneak peek.
+
+You could write a view
+for a webpage
+and include a lot of HTML
+in your Python.
+HTML is the markup language
+of internet pages
+that we use
+to describe the format
+of a page.
+
+This view might look like:
+
+```python
+from django.http import HttpResponse
+
+def my_html_view(request):
+ response_content = """
+
+ Hello World!
+
+ This is a demo.
+
+
+ """
+ return HttpResponse(response_content)
+```
+
+While this works,
+it has many shortcomings.
+
+1. The HTML chunk isn't reusable by other views.
+ That doesn't matter much for this small example,
+ but it would be a huge problem
+ when you try to make many views
+ that use a lot of markup
+ and need to share a common look.
+2. The mixing of Python and HTML is going to get messy.
+ Need proof?
+ Go look at computing history
+ and learn about {{< extlink "https://en.wikipedia.org/wiki/Common_Gateway_Interface" "CGI" >}}.
+ It wasn't pretty.
+3. How can you join pieces of HTML together?
+ Not easily.
+
+With templates,
+we can separate the layout
+from the logic.
+
+```python
+# application/views.py
+from django.shortcuts import render
+
+def my_html_view(request):
+ return render(
+ request,
+ "template.html",
+ {}
+ )
+```
+
+And we would have another file named `template.html` containing:
+
+```html
+
+Hello World!
+
+ This is a demo.
+
+
+```
+
+{{< web >}}
+The important part for this article is not about the templates themselves.
+{{< /web >}}
+{{< book >}}
+The important part for this chapter is not about the templates themselves.
+{{< /book >}}
+What's worth noting is that `render`
+loads the content from `template.html`,
+gets the output,
+and adds that output to an `HttpResponse` instance.
+
+That wraps up `HttpRequest` and `HttpResponse`.
+With those building blocks,
+we can now look at other ways
+that you can make Django views
+for your project.
+
+## View Classes
+
+By now we've seen this relationship with views:
+
+```text
+HttpRequest -> view -> HttpResponse
+```
+
+Views do not need to be functions exclusively.
+Django also provides tools
+to make views out of classes.
+These types of views derive
+from Django's `View` class.
+
+When you write a class-based view
+(often abbreviated to CBVs),
+you add instance methods
+that match up
+with HTTP methods.
+Let's see an example:
+
+```python
+# application/views.py
+from django.http import HttpResponse
+from django.views.generic.base import View
+
+class SampleView(View):
+ def get(self, request, *args, **kwargs):
+ return HttpResponse("Hello from a CBV!")
+```
+
+The `get` method
+on the class
+corresponds to a `GET` HTTP request.
+`*args` and `**kwargs` are a common convention
+in Python
+to make a method or function
+that accepts any number
+of positional or keyword based arguments.
+We need these to match the expect method signature
+that Django requires for CBVs.
+Similarly,
+you would write a `post` method
+to respond to a `POST` HTTP request
+and so on.
+With that view defined,
+we can connect it to a URLconf:
+
+```python
+# project/urls.py
+from django.urls import path
+
+from application.views import SampleView
+
+urlpatterns = [
+ path("", SampleView.as_view()),
+]
+```
+
+Note that we don't pass `SampleView`
+to `path` as is.
+`path` expects a callable object,
+so we must call `as_view`,
+a class method
+that returns a function
+that will call the code
+in our class.
+
+At this point,
+I would be suitably unimpressed
+if I were in your shoes.
+Why would we add all
+this boilerplate code
+when you can make a function
+and be done?
+If this were the full story,
+I would absolutely agree with you.
+A class-based view doesn't add much
+beyond the function-based version.
+If anything,
+CBVs have more to remember,
+so they are probably more confusing.
+
+Where class-based views begin
+to shine
+is when using some other classes
+beyond the initial `View` class.
+
+Django includes a host
+of class-based views
+to use for a variety
+of purposes.
+We can explore a few
+of them
+with our limited exposure
+to the full framework so far.
+
+## Out Of The Box Views
+
+I won't exhaustively cover all the class-based views
+because there are many.
+Also,
+{{< web >}}
+if you're joining this article series
+from the beginning
+and have never done Django before,
+{{< /web >}}
+{{< book >}}
+if you have never done Django before,
+{{< /book >}}
+then there will still be holes
+in your knowledge
+(which we will plug together!),
+and some of the views will not make much sense.
+
+### RedirectView
+
+Use `RedirectView` to send users
+of your site
+to a different place.
+You *could* make a view
+that returns an `HttpResponseRedirect` instance,
+but this class-based view can handle that for you.
+
+In fact,
+you can use `RedirectView`
+without subclassing it.
+Check this out:
+
+```python
+# project/urls.py
+from django.urls import path
+from django.views.generic.base import RedirectView
+
+from application.views import NewView
+
+urlpatterns = [
+ path("old-view-path/",
+ RedirectView.as_view(url="https://www.somewhereelse.com")),
+ path("other-old-path/", RedirectView.as_view(pattern_name='new-view')),
+ path("new-path/", NewView.as_view(), name='new-view'),
+]
+```
+
+`RedirectView` can use `url`
+for a full URL,
+or it can use `pattern_name`
+if you need to route
+to a view
+that moved somewhere else
+in your project.
+
+`as_view` is what lets us avoid subclassing `RedirectView`.
+The arguments passed to `as_view` override any class attributes.
+The following two `RedirectView` uses are equivalent:
+
+```python
+# project/urls.py
+from django.urls import path
+from django.views.generic.base import RedirectView
+
+from application.views import NewView
+
+class SubclassedRedirectView(RedirectView):
+ pattern_name = 'new-view'
+
+urlpatterns = [
+ path("old-path/", SubclassedRedirectView.as_view()),
+ # The RedirectView below acts like SubclassedRedirectView.
+ path("old-path/", RedirectView.as_view(pattern_name='new-view')),
+ path("new-path/", NewView.as_view(), name='new-view'),
+]
+```
+
+### TemplateView
+
+{{< web >}}
+Earlier in the article,
+{{< /web >}}
+{{< book >}}
+Earlier in the chapter,
+{{< /book >}}
+we briefly saw how to separate web page layout
+from the logic needed
+to build a page
+with templates.
+
+Templates are so commonly used
+that Django provides a class
+that knows how to produce a response
+with nothing more than a template name.
+
+An example looks like:
+
+```python
+# application/views.py
+from django.views.generic.base import TemplateView
+
+class HomeView(TemplateView):
+ template_name = 'home.html'
+```
+
+We will look at template views
+in greater detail
+{{< web >}}
+in the next article
+{{< /web >}}
+{{< book >}}
+in the next chapter
+{{< /book >}}
+when we dive into templates.
+
+### Other View Classes
+
+Django's other class-based views serve a variety
+of purposes.
+Django has views that will:
+
+* Display and handle HTML forms
+ so users can input data
+ and send the data
+ to the application.
+* Pull data from a database
+ and show an individual record
+ to the user
+ (e.g., a webpage to see facts about a particular movie).
+* Pull data from a database
+ and show information
+ from a collection of records
+ to the user
+ (e.g., showing the cast of actors from a movie).
+* Show data from specific time ranges
+ like days, weeks, and months.
+
+As we continue to explore Django,
+We will discuss these views
+when their related topic (like forms) is the primary subject
+{{< web >}}
+of an article.
+{{< /web >}}
+{{< book >}}
+of a chapter.
+{{< /book >}}
+For now,
+when you're developing your own views,
+try to remember that Django probably has a class-based view
+to aid your work.
+
+## Useful View Decorators And Mixins
+
+Before we finish the tour
+of views,
+let's discuss some useful decorators and mixin classes.
+
+Decorators are a feature
+of Python
+(and many other languages)
+that let you extend a function
+with additional capabilities.
+A decorator can wrap a view function
+to provide new behavior
+to a view.
+Decorators are helpful
+when you have common functionality
+that you want to add to many views
+without copying and pasting a lot of code.
+
+Mixin classes serve a very similar purpose
+as decorators,
+but use Python's multiple inheritance feature
+of classes
+to "mix in" the new behavior
+with an existing class-based view.
+
+### Decorators To Know
+
+When you work
+with function-based views,
+there is a challenge
+when handling different HTTP methods.
+By default,
+a function based view can receive requests
+from *any* HTTP method.
+Some views will handle multiple methods like:
+
+```python
+# application/views.py
+from django.http import (
+ HttpResponse,
+ HttpResponseNotAllowed,
+)
+
+def multi_method_view(request):
+ if request.method == 'GET':
+ return HttpResponse('Method was a GET.')
+ elif request.method == 'POST':
+ return HttpResponse('Method was a POST.')
+ return HttpResponseNotAllowed()
+```
+
+This view uses the `request` instance `method` attribute
+to check the request's HTTP method.
+What if you only want your view
+to respond to one HTTP method?
+Let's say you only want to respond to a POST.
+We could write:
+
+```python
+# application/views.py
+from django.http import (
+ HttpResponse,
+ HttpResponseNotAllowed,
+)
+
+def guard_clause_view(request):
+ if request.method != 'POST':
+ return HttpResponseNotAllowed()
+
+ return HttpResponse('Method was a POST.')
+
+# OR
+
+def if_clause_view(request):
+ if request.method == 'POST':
+ return HttpResponse('Method was a POST.')
+ else:
+ return HttpResponseNotAllowed()
+```
+
+Both techniques work,
+but the code is a little messier
+because of the extra indentation.
+Instead, we can use the `require_POST` decorator
+and let Django check the method
+for us.
+
+```python
+# application/views.py
+from django.http import HttpResponse
+from django.view.decorators.http import require_POST
+
+@require_POST
+def the_view(request):
+ return HttpResponse('Method was a POST.')
+```
+
+This version states the expectation up front
+with the decorator
+and declares the contract
+that the view will work with.
+If a user tries a different method
+(like a `GET`),
+then Django will respond
+with HTTP status code `405`,
+which is an error code for "method not allowed."
+
+Another common decorator you may encounter is the `login_required` decorator.
+When we get to the subject
+of user management,
+you'll see that we can make a protected view
+for an app
+by including this decorator.
+
+```python
+# application/views.py
+from django.contrib.auth.decorators import login_required
+from django.http import HttpResponse
+
+@login_required
+def the_view(request):
+ return HttpResponse('This view is only viewable to authenticated users.')
+```
+
+Any unauthenticated user will be redirected automatically
+to the login page
+for your web app.
+
+A final example
+of a useful built-in decorator
+is `user_passes_test`.
+This is another decorator used
+with the user management system
+that lets us control *which* users should be allowed
+to access a view.
+For instance,
+we could make a view that only staff-level users could access.
+
+```python
+# application/views.py
+from django.contrib.auth.decorators import user_passes_test
+from django.http import HttpResponse
+
+@user_passes_test(lambda user: user.is_staff)
+def the_view(request):
+ return HttpResponse('Only visible to staff users.')
+```
+
+The decorator takes a callable
+that will accept a single argument
+of a user object.
+The view will only be accessible
+if the return value of the test callable evaluates to `True`.
+
+What I'm trying to show
+with these examples
+is how single decorators can quickly augment your views
+with new features.
+And, because of how decorators work to wrap functions,
+you can "stack" these together.
+
+```python
+# application/views.py
+from django.contrib.auth.decorators import user_passes_test
+from django.http import HttpResponse
+from django.view.decorators.http import require_POST
+
+@require_POST
+@user_passes_test(lambda user: user.is_staff)
+def the_view(request):
+ return HttpResponse('Only staff users may POST to this view.')
+```
+
+### Mixins To Know
+
+Mixin classes are to class-based views
+as decorators are to function-based views.
+This isn't *completely* true
+since class-based views can also use decorators,
+but it should give you an idea
+of where mixins fit.
+
+Like the `login_required`
+and `user_passes_test` decorators,
+we have mixin equivalents
+of `LoginRequiredMixin`
+and `UserPassesTestMixin`.
+Maybe you have some template views
+that should only be accessible
+to authenticated users
+or staff-level users.
+Those views could look like:
+
+```python
+# application/views.py
+from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
+from django.views.generic.base import TemplateView
+
+class HomeView(LoginRequiredMixin, TemplateView):
+ template_name = 'home.html'
+
+class StaffProtectedView(UserPassesTestMixin, TemplateView):
+ template_name = 'staff_eyes_only.html'
+
+ def test_func(self):
+ return self.request.user.is_staff
+```
+
+You can see that these views are similar
+to their decorator counterparts
+with a slightly different usage pattern.
+
+One thing worth noting
+with mixins
+is their placement.
+Because of the way
+that Python handles multiple inheritance,
+you should be sure to include mixin classes
+to the left
+in the list of inherited base classes.
+This will ensure that Python will behave appropriately
+with these classes.
+The exact reason for this placement is
+because of Python's method resolution order (MRO) rules
+when using multiple inheritance.
+MRO is outside of our scope,
+but that's what you can search for
+if you want to learn more.
+
+There are plenty
+of other mixin classes.
+Most of Django's built-in class-based views are constructed
+by composing various mixin classes together.
+If you'd like to see how they are constructed,
+check out {{< extlink "https://ccbv.co.uk/" "Classy Class-Based Views" >}},
+a site showing the built-in CBVs
+and the mixins and attributes available
+to those classes.
+
+## Summary
+
+That's a wrap on view fundamentals.
+We've looked at:
+
+* View functions
+* `HttpRequest` and `HttpResponse`
+* View classes
+* Some built-in supporting views
+* Decorators and mixins that supercharge views.
+
+{{< web >}}
+In the next article,
+{{< /web >}}
+{{< book >}}
+In the next chapter,
+{{< /book >}}
+we'll see how views can mix static layout
+with the dynamic data we provide
+by using templates.
+Templates are the workhorse
+for your Django-based user interfaces.
+We're going to see:
+
+* How to set up templates for your site
+* Ways to call templates from views
+* How to use data
+* How to handle logic
+* Built-in functions available to templates
+* Customizing templates with your own code extensions
+
+{{< web >}}
+If you'd like to follow along
+with the series,
+please feel free to sign up
+for my newsletter
+where I announce all of my new content.
+If you have other questions,
+you can reach me online
+on Twitter
+where I am
+{{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
+{{< /web >}}
+
diff --git a/content/understand-django/2020-04-02-templates-user-interfaces.pt.md b/content/understand-django/2020-04-02-templates-user-interfaces.pt.md
new file mode 100644
index 00000000..144e4431
--- /dev/null
+++ b/content/understand-django/2020-04-02-templates-user-interfaces.pt.md
@@ -0,0 +1,1355 @@
+---
+title: "Templates For User Interfaces"
+description: >-
+ When your Django application
+ sends back a response
+ with your user interface,
+ templates are the tool you'll use
+ to produce that user interface.
+ This article looks
+ at what templates are
+ and how to use them.
+image: img/django.png
+type: post
+categories:
+ - Python
+ - Django
+tags:
+ - Python
+ - Django
+ - templates
+
+---
+
+{{< web >}}
+In the previous
+[Understand Django]({{< ref "/understand-django/_index.md" >}})
+article,
+we looked at the fundamentals
+of using views in Django.
+This article will focus
+on templates.
+{{< /web >}}
+Templates are your primary tool
+in a Django project
+for generating a user interface.
+With templates,
+you'll be able to build the pages
+that users will see when they visit your web app.
+Let's see how templates hook into views
+and what features Django provides
+with its template system.
+
+{{< understand-django-series "templates" >}}
+
+## Set Up Templates
+
+We need a place for templates to live.
+Templates are static files
+that Django will fill in
+with data.
+In order to use those files,
+we must instruct Django
+on where to find them.
+
+Like most parts of Django,
+this configuration is
+in your project's settings file.
+After you use `startproject`,
+you can find a section
+in your settings file
+that will be called `TEMPLATES`.
+The section should look something like:
+
+```python
+# project/settings.py
+
+TEMPLATES = [{
+ 'BACKEND': 'django.template.backends.django.DjangoTemplates',
+ 'DIRS': [],
+ 'APP_DIRS': True,
+ 'OPTIONS': {
+ 'context_processors': [
+ 'django.template.context_processors.debug',
+ 'django.template.context_processors.request',
+ 'django.contrib.auth.context_processors.auth',
+ 'django.contrib.messages.context_processors.messages',
+ ],
+ },
+}]
+```
+
+Django's template system can use multiple template backends.
+The backends dictate how your templates will work.
+I would recommend sticking
+with the default Django template language.
+This language has the tightest integration
+with the framework
+and the strongest support.
+
+The next thing to notice is `APP_DIRS`
+with its value of `True`.
+For the Django template language,
+setting this value to `True` will cause Django
+to look for template files
+within a `templates` directory
+in each Django application
+in your project.
+Note that this also includes any third party applications
+so you should probably leave this set to `True`.
+
+So,
+where should *your* templates go?
+There are different schools
+of thought
+in the Django community.
+Some developers believe in having all templates
+within applications.
+Others ascribe to having all your project's templates
+in a single directory.
+I'm in this second category of developers.
+I find it valuable
+to keep all
+of the templates for my entire project
+within a single directory.
+
+From my perspective,
+keeping templates in a single directory
+makes it very clear
+where all the layout and UI
+in your system will live.
+To use that pattern,
+we must set the `DIRS` variable
+with the directory
+that we want Django to include.
+I recommend keeping a `templates` directory
+at the root of your project.
+If you do that,
+your `DIRS` value will change
+to something like:
+
+```python
+# project/settings.py
+
+TEMPLATES = [
+...
+ "DIRS": [BASE_DIR / "templates"],
+...
+]
+```
+
+Finally,
+there is `OPTIONS`.
+Each backend can accept a variety
+of options.
+`startproject` sets a number of context processors.
+We'll come back to context processors later
+{{< web >}}
+in this article.
+{{< /web >}}
+{{< book >}}
+in this chapter.
+{{< /book >}}
+
+With your templates set up,
+you're ready to go!
+
+## Using Templates With Render
+
+Django builds your user interface
+by *rendering* a template.
+The idea behind rendering is
+that dynamic data is combined
+with a static template file
+to produce a final output.
+
+To produce an `HttpResponse`
+that contains rendered output,
+we use the `render` function.
+Let's see an example
+in the form of a function-based view (FBV).
+
+```python
+# application/views.py
+
+from django.shortcuts import render
+
+def hello_view(request):
+ context = {'name': 'Johnny'}
+ return render(
+ request,
+ 'hello.txt',
+ context
+ )
+```
+
+In this example,
+the view would use a template located
+in `templates/hello.txt`
+which could contain:
+
+```txt
+Hello {{ name }}
+```
+
+When this view responds
+to a request,
+a user would see "Hello Johnny"
+in their browser.
+There are some interesting things to note
+about this example.
+
+1. The template can be any plain text file type.
+ Most often we will use HTML to make a user interface
+ so you will often see `some_template.html`,
+ but the Django template system can render on any type.
+2. In the process of rendering,
+ Django took the context data dictionary
+ and used its keys as variable names
+ in the template.
+ Because of special double curly brace syntax,
+ the template backend swapped out `{{ name }}`
+ for the literal value of "Johnny"
+ that was in the context.
+
+This idea of mixing context and static layout is the core concept
+of working with templates.
+{{< web >}}
+The rest of this article builds
+{{< /web >}}
+{{< book >}}
+The rest of this chapter builds
+{{< /book >}}
+on this root concept
+and shows what else is possible
+in the Django template language.
+
+As an aside,
+HTML is a topic that we are not going to explore directly.
+HTML, the Hypertext Markup Language, is the language used
+on the web
+to describe the structure of a page.
+HTML is composed of tags
+and many of these tags work in pairs.
+For example,
+to make a *paragraph*,
+you can use a `p` tag,
+which is represented
+by wrapping `p` with greater than and less than symbols
+to form the "opening" tag.
+The "closing" tag is similar,
+but it includes a forward slash.
+
+```html
+This is a paragraph example.
+```
+
+{{< web >}}
+From the last article,
+{{< /web >}}
+{{< book >}}
+From the last chapter,
+{{< /book >}}
+you may recall seeing the `TemplateView`.
+In those examples,
+we provided a template name,
+and I declared that Django would take care
+of the rest.
+Now you can start to understand
+that Django takes the template name
+and calls code similar to `render`
+to provide an `HttpResponse`.
+Those examples were missing context data
+to combine with the template.
+A fuller example replicating the `hello_view` function-based view
+as a class-based-view would look like:
+
+```python
+# application/views.py
+
+from django.views.generic.base import TemplateView
+
+class HelloView(TemplateView):
+ template_name = 'hello.txt'
+
+ def get_context_data(
+ self,
+ *args,
+ **kwargs
+ ):
+ context = super().get_context_data(
+ *args, **kwargs)
+ context['name'] = 'Johnny'
+ return context
+```
+
+This example uses `get_context_data`
+so that we can insert our "dynamic" data
+into the rendering system
+to give us the response we want.
+
+In a real application,
+a lot of the code that we need to write focuses
+on building up a truly dynamic context.
+I'm using static data in these examples
+to keep the mechanics of the template system clear.
+When you see me use `context`,
+try to imagine more complex data building
+to create a user interface.
+
+Those are the fundamentals of rendering.
+We'll now turn our attention
+to what the Django template language is capable of.
+
+## Templates In Action
+
+When using templates,
+we take context data
+and insert it
+into the placeholders
+within the template.
+
+Template variables are the most basic form
+of filling placeholders with context.
+The previous section showed an example
+by using the `name` variable.
+The context dictionary contains a `name` key,
+whose value appears anywhere in the template
+where that key is surrounded by double curly braces.
+
+We can also use a dot access
+when the context data is more complex.
+Let's say your template gets context like:
+
+```python
+context = {
+ 'address': {
+ 'street': '123 Main St.',
+ 'city': 'Beverly Hills',
+ 'state': 'CA',
+ 'zip_code': '90210',
+ }
+}
+```
+
+Your Django template *won't* work
+if you try to access this context data
+like a regular dictionary
+(e.g., `{{ address['street'] }}`).
+Instead,
+you would use dot notation
+to get to the data
+in the dictionary.
+
+```txt
+The address is:
+ {{ address.street }}
+ {{ address.city }}, {{ address.state }} {{ address.zip_code}}
+```
+
+This would render as:
+
+```txt
+The address is:
+ 123 Main St.
+ Beverly Hills, CA 90210
+```
+
+Django templates also try
+to be flexible
+with the types of context data.
+You could also pass in a Python class instance
+like an `Address` class
+with attributes
+that are the same as the keys
+in our previous dictionary.
+The template would work the same.
+
+The core template language also includes some standard programming logic keywords
+by using *tags*.
+Template tags look like `{% some_tag %}`
+whereas template variables look like `{{ some_variable }}`.
+Variables are meant to be placeholders
+to fill in,
+but tags offer more power.
+
+We can start
+with two core tags, `if` and `for`.
+
+The `if` tag is for handling conditional logic
+that your template might need.
+
+{{< web >}}
+```django
+{% if user.is_authenticated %}
+ Welcome, {{ user.username }}
+{% endif %}
+```
+{{< /web >}}
+{{< book >}}
+```djangotemplate
+{% if user.is_authenticated %}
+ Welcome, {{ user.username }}
+{% endif %}
+```
+{{< /book >}}
+
+This example will only include this welcome message HTML header tag
+when the user is logged in
+to the application.
+We started the example
+with an `if` tag.
+Observe that the `if` tag requires a closing `endif` tag.
+Templates must respect whitespace
+since your layout might depend
+on that whitespace.
+The template language can't use whitespace
+to indicate scope
+like it can with Python
+so it uses closing tags instead.
+As you might guess,
+there are also `else` and `elif` tags
+that are accepted inside
+of an `if`/`endif` pair.
+
+{{< web >}}
+```django
+{% if user.is_authenticated %}
+ Welcome, {{ user.username }}
+{% else %}
+ Welcome, guest
+{% endif %}
+```
+{{< /web >}}
+{{< book >}}
+```djangotemplate
+{% if user.is_authenticated %}
+ Welcome, {{ user.username }}
+{% else %}
+ Welcome, guest
+{% endif %}
+```
+{{< /book >}}
+
+In this case, only one of the header tags will render
+depending on whether the user is authenticated or not.
+
+The other core tag
+to consider
+is the `for` loop tag.
+A `for` loop
+in Django templates
+behaves as you might expect.
+
+{{< web >}}
+```django
+Prices:
+
+{% for item in items %}
+ {{ item.name }} costs {{ item.price }}.
+{% endfor %}
+
+```
+{{< /web >}}
+{{< book >}}
+```djangotemplate
+Prices:
+
+{% for item in items %}
+ {{ item.name }} costs {{ item.price }}.
+{% endfor %}
+
+```
+{{< /book >}}
+
+Django will loop over iterables
+like lists
+and let users output template responses
+for each entry in an iterable.
+If the example above had a list
+of `items`
+in the context like:
+
+```python
+items = [
+ {'name': 'Pizza', 'price': '$12.99'},
+ {'name': 'Soda', 'price': '$3.99'},
+]
+```
+
+Then the output would look roughly like:
+
+```html
+Prices:
+
+ Pizza costs $12.99.
+ Soda costs $3.99.
+
+```
+
+Occasionally,
+you may want to take some specific action
+on a particular element
+in the `for` loop.
+Python's built in `enumerate` function isn't available directly
+in templates,
+but a special variable called `forloop` is available
+inside of a `for` tag.
+This `forloop` variable has some attributes
+like `first` and `last`
+that you can use to make templates behave differently
+on certain loop iterations.
+
+{{< web >}}
+```django
+Counting:
+{% for number in first_three_numbers %}
+ {{ number }}{% if forloop.last %} is last!{% endif %}
+{% endfor %}
+```
+{{< /web >}}
+{{< book >}}
+```djangotemplate
+Counting:
+{% for number in first_three_numbers %}
+ {{ number }}{% if forloop.last %} is last!{% endif %}
+{% endfor %}
+```
+{{< /book >}}
+
+This example would produce:
+
+```txt
+Counting:
+ 1
+ 2
+ 3 is last!
+```
+
+Equipped with variables,
+`if` tags,
+and `for` tags,
+you should now have the ability to make some fairly powerful templates,
+but there's more!
+
+### More Context On Context
+
+In the setup
+of the templates settings,
+we glossed over context processors.
+Context processors are a valuable way
+to extend the context
+that is available
+to your templates
+when they are rendered.
+
+Here's the set of context processors
+that Django's `startproject` command brings in
+by default.
+
+```python
+'context_processors': [
+ 'django.template.context_processors.debug',
+ 'django.template.context_processors.request',
+ 'django.contrib.auth.context_processors.auth',
+ 'django.contrib.messages.context_processors.messages',
+],
+```
+
+Context processors are functions
+(technically, callables, but let's focus on functions)
+that receive an `HttpRequest`
+and must return a dictionary.
+The returned dictionary merges
+with any other context
+that will be passed to your template.
+
+Conceptually,
+when preparing to render
+and given a `context` dictionary
+that was passed to `render`,
+the template system will do something like:
+
+```python
+for processor in context_processors:
+ context.update(processor(request))
+
+# Continue on to template rendering
+```
+
+The actual code in the template system is more complex
+than this concept code sketch,
+but not by much!
+
+We can look
+at the actual definition of the `request` context processor included
+in that default list.
+
+```python
+# django/template/context_processors.py
+
+def request(request):
+ return {'request': request}
+```
+
+That's it!
+Because of this context processor,
+the `request` object will be available
+as a variable
+to any template
+in your project.
+That's super powerful.
+
+
+
+The "dark side" of context processors is
+that they run for all requests.
+If you write a context processor
+that is slow and does a lot of computation,
+*every request* will suffer
+that performance impact.
+So use context processors carefully.
+
+### Reusable Chunks Of Templates
+
+Now let's talk about one of the powerhouse features
+of the template system: reusable pieces.
+
+Think about a website.
+Most pages have a similar look and feel.
+They do this by repeating a lot of the same HTML,
+which is Hypertext Markup Language
+that defines the structure
+of a page.
+These pages also use the same CSS, Cascading Style Sheets,
+which define the styles that shape the look
+of the page elements.
+
+Imagine you're asked to manage a site
+and you need to create two separate pages.
+The homepage looks like:
+
+```html
+
+
+
+
+
+
+ Hello from the Home page
+
+
+```
+
+And here is a page to learn about the company
+behind the website.
+
+```html
+
+
+
+
+
+
+ Learn about our company
+
+
+```
+
+These examples are tiny amounts of HTML,
+but what if you're asked to change the stylesheet
+from `styles.css`
+to a new stylesheet made
+by a designer called `better_styles.css`?
+You would have to update both places.
+Now think if there were 2,000 pages
+instead of 2 pages.
+Making big changes quickly across a site would be virtually impossible!
+
+Django helps you avoid this scenario entirely
+with a few tags.
+Let's make a new template called `base.html`.
+
+{{< web >}}
+```django
+
+
+
+
+
+
+ {% block main %}{% endblock %}
+
+
+```
+{{< /web >}}
+{{< book >}}
+```djangotemplate
+
+
+
+
+
+
+ {% block main %}{% endblock %}
+
+
+```
+{{< /book >}}
+
+We've created a reusable template with the `block` tag!
+We can fix up our homepage
+to use this new template.
+
+{{< web >}}
+```django
+{% extends "base.html" %}
+
+{% block main %}
+ Hello from the Home page
+{% endblock %}
+```
+{{< /web >}}
+{{< book >}}
+```djangotemplate
+{% extends "base.html" %}
+
+{% block main %}
+ Hello from the Home page
+{% endblock %}
+```
+{{< /book >}}
+
+This new version of the homepage *extends* the base template.
+All the template had to do was define its own version
+of the `main` block
+to fill in the content.
+We could do the exact same thing with the about page.
+
+If we revisit the task of replacing `styles.css`
+with `better_styles.css`,
+we can make the update in `base.html`
+and have that change apply
+to any templates
+that extend it.
+Even if there were 2,000 pages
+that all extended from `base.html`,
+changing the stylesheet would still be one line
+of code
+to change
+for an entire site.
+
+That's the power of Django's template extension system.
+Use `extend` when you need content
+that is mostly the same.
+Add a `block` section whenever you need to customize an extended page.
+You can extend a page by including multiple types of blocks.
+The example only shows a `main` block,
+but you might have pages that customize a `sidebar`, `header`, `footer`,
+or whatever might vary.
+
+Another powerful tool for reuse is the `include` tag.
+The `include` tag is useful
+when you want to extract some chunk
+of template
+that you want to use
+in multiple locations.
+You may want to use `include` to:
+
+1. Keep templates tidy.
+ You can break a large template up into small pieces
+ that are more manageable.
+2. Use a template fragment
+ in different parts of your site.
+ Maybe you have a piece of template
+ that should only appear on a few pages.
+
+Coming back to our website example,
+imagine that `base.html` grew to be 20,000 lines long.
+Navigating to the right part
+of the template
+to make changes
+is now harder.
+We can decompose the template
+into smaller pieces.
+
+{{< web >}}
+```django
+
+
+ {% include "head.html" %}
+
+ {% include "navigation.html" %}
+ {% block main %}{% endblock %}
+
+ {% include "footer.html" %}
+
+```
+{{< /web >}}
+{{< book >}}
+```djangotemplate
+
+
+ {% include "head.html" %}
+
+ {% include "navigation.html" %}
+ {% block main %}{% endblock %}
+
+ {% include "footer.html" %}
+
+```
+{{< /book >}}
+
+The `include` tag can move those extra pieces around.
+By providing a good name for your templates,
+if you needed to change the structure of some section
+like navigation,
+you could go to the template
+with the appropriate name.
+That template file would focus
+on only the element that you need to change.
+
+`block`, `extends`, and `include` are core tags
+for keeping your user interface code
+from sprawling all over the place
+with lots of duplication.
+
+Next, let's talk about more
+of Django's built-in template tags
+that can supercharge your UI.
+
+## The Templates Toolbox
+
+The Django documentation includes
+a {{< extlink "https://docs.djangoproject.com/en/4.1/ref/templates/builtins/" "large set of built-in tags" >}}
+that you can use
+in your projects.
+We aren't going to cover all of them,
+but I'll focus
+on a few tags
+to give you a flavor
+of what is available.
+
+One of the most used built-in tags
+aside from what we've already covered
+is the `url` tag.
+{{< web >}}
+Recall from the article
+{{< /web >}}
+{{< book >}}
+Recall from the chapter
+{{< /book >}}
+on URLs
+that you can get the URL
+to a named view
+by using the `reverse` function.
+What if you wanted to use the URL
+in your template?
+You could do this:
+
+```python
+# application/views.py
+
+from django.shortcuts import render
+from django.urls import reverse
+
+def the_view(request):
+ context = {
+ 'the_url': reverse('a_named_view')
+ }
+ return render(
+ request,
+ 'a_template.html',
+ context
+ )
+```
+
+While this works,
+it's tedious to have to route all URLs
+through the context.
+Instead,
+our template can directly create the proper URL.
+Here's what `a_template.html` might look like instead:
+
+{{< web >}}
+```django
+Go to a named view
+```
+{{< /web >}}
+{{< book >}}
+```djangotemplate
+Go to a named view
+```
+{{< /book >}}
+
+The `url` tag is the template equivalent
+of the `reverse` function.
+Like its `reverse` counterpart,
+`url` can accept args or kwargs
+for routes
+that expect other variables.
+`url` is an incredibly useful tool
+and one that you will probably reach for many times
+as you build your user interface.
+
+Another useful tag is the `now` tag.
+`now` is a convenient method
+to display information
+about the current time.
+Using what Django calls *format specifiers*,
+you can tell your template how to display the current time.
+Want to add a current copyright year to your website?
+No problem!
+
+{{< web >}}
+```django
+© {% now "Y" %} Your Company LLC.
+```
+{{< /web >}}
+{{< book >}}
+```djangotemplate
+© {% now "Y" %} Your Company LLC.
+```
+{{< /book >}}
+
+One final built-in tag to consider is the `spaceless` tag.
+HTML is *partially* sensitive to whitespace.
+There are some frustrating circumstances
+where this whitespace sensitivity can ruin your day
+when building a user interface.
+Can you make a pixel perfect navigation menu
+for your site with an unordered list?
+Maybe. Consider this:
+
+```html
+
+```
+
+The indented whitespace on those list items
+(or the new line characters that follow them)
+might cause you trouble
+when working with CSS.
+Knowing that the whitespace can affect layout,
+we can use `spaceless` like so:
+
+{{< web >}}
+```django
+{% spaceless %}
+
+{% endspaceless %}
+```
+{{< /web >}}
+{{< book >}}
+```djangotemplate
+{% spaceless %}
+
+{% endspaceless %}
+```
+{{< /book >}}
+
+This neat little template tag will remove all the spaces
+between HTML tags
+so your output looks like:
+
+```html
+
+```
+
+By removing the extra space,
+you may get a more consistent experience
+with your CSS styling
+and save yourself some frustration.
+{{< web >}}
+(I had to trim the output to fit better on the screen.)
+{{< /web >}}
+
+There is another kind of built-in
+that we have not looked at yet.
+These alternative built-in functions are called **filters**.
+Filters change the output of variables
+in your templates.
+The filter syntax is a bit interesting.
+It looks like:
+
+{{< web >}}
+```django
+Here's a filter example: {{ a_variable|some_filter:"filter arguments" }}
+```
+{{< /web >}}
+{{< book >}}
+```djangotemplate
+Here's a filter example:
+{{ a_variable|some_filter:"filter arguments" }}
+```
+{{< /book >}}
+
+The important element is the pipe character directly
+after a variable.
+This character signals to the template system
+that we want to modify the variable
+with some kind of transformation.
+Also observe that filters are used
+between double curly braces
+instead of the `{%` syntax
+that we've seen with tags.
+
+A very common filter is the `date` filter.
+When you pass a Python `datetime` instance
+in the context,
+you can use the `date` filter
+to control the format
+of the datetime.
+The `date` {{< extlink "https://docs.djangoproject.com/en/4.1/ref/templates/builtins/#date" "documentation" >}} shows
+what options you can use
+to modify the format.
+
+{{< web >}}
+```django
+{{ a_datetime|date:"Y-m-d" }}
+```
+{{< /web >}}
+{{< book >}}
+```djangotemplate
+{{ a_datetime|date:"Y-m-d" }}
+```
+{{< /book >}}
+
+If `a_datetime` was an instance of April Fools' Day,
+then it could return a string like `2020-04-01`.
+The `date` filter has many specifiers
+that will enable you to produce most
+of the date formatting outputs
+you could think of.
+
+`default` is a useful filter
+for when your template value evaluates to `False`.
+This is perfect when you've got a variable
+with an empty string.
+The example below outputs "Nothing to see here"
+if the variable was Falsy.
+
+{{< web >}}
+```django
+{{ a_variable|default:"Nothing to see here." }}
+```
+{{< /web >}}
+{{< book >}}
+```djangotemplate
+{{ a_variable|default:"Nothing to see here." }}
+```
+{{< /book >}}
+
+Falsy is a concept in Python
+that describes anything
+that Python will evaluate as false
+in a boolean expression.
+Empty strings, empty lists, empty dicts, empty sets, `False`, and `None`
+are all common Falsy values.
+
+`length` is a simple filter
+for lists.
+`{{ a_list_variable|length }}` will produce a number.
+It is the Django template equivalent to the `len` function.
+
+I like the `linebreaks` filter a lot.
+If you create a form
+{{< web >}}
+(which we'll explore in the next article)
+{{< /web >}}
+{{< book >}}
+(which we'll explore in the next chapter)
+{{< /book >}}
+and accept a text area field where the user is allowed
+to provide newlines,
+then the `linebreaks` filter allows you
+to display those newlines later
+when rendering the user's data.
+By default,
+HTML will not show new line characters as intended.
+The `linebreaks` filter will convert `\n`
+to a ` ` HTML tag.
+Handy!
+
+Before moving on,
+let's consider two more.
+
+`pluralize` is a convenient filter
+for the times when your text considers counts
+of things. Consider a count of items.
+
+{{< web >}}
+```django
+{{ count_items }} item{{ count_items|pluralize }}
+```
+{{< /web >}}
+{{< book >}}
+```djangotemplate
+{{ count_items }} item{{ count_items|pluralize }}
+```
+{{< /book >}}
+
+The `pluralize` filter will do the right thing
+if there are zero, one, or more items
+in the list.
+
+```txt
+0 items
+1 item
+2 items
+3 items
+(and so on)
+```
+
+Be aware that `pluralize` can't handle irregular plurals
+like "mice" for "mouse."
+
+The final filter in our tour is the `yesno` filter.
+`yesno` is good for converting `True|False|None`
+into a meaningful text message.
+Imagine we're making an application
+for tracking events
+and a person's attendance is one
+of those three values.
+Our template might look like:
+
+{{< web >}}
+```django
+{{ user.name }} has {{ user_accepted|yesno:"accepted,declined,not RSVPed" }}.
+```
+{{< /web >}}
+{{< book >}}
+```djangotemplate
+{{ user.name }} has {{ user_accepted|yesno:"accepted,declined,not RSVPed" }}.
+```
+{{< /book >}}
+
+Depending on the value of `user_accepted`,
+the template will display something meaningful
+to a reader.
+
+There are so many built-ins
+that it's really hard to narrow down my favorites.
+Check out the full list
+to see what might be useful for you.
+
+What if the built-ins don't cover what you need?
+Have no fear,
+Django lets you make custom tags and filters
+for your own purposes.
+We'll see how next.
+
+## Build Your Own Lightsaber In Templates
+
+When you need to build your own template tags or filters,
+Django gives you the tools to make what you need.
+
+There are three major elements to working with custom tags:
+
+1. Defining your tags in a place that Django expects.
+2. Registering your tags with the template engine.
+3. Loading your tags in a template so they can be used.
+
+The first step is to put the tags
+in the correct location.
+To do that,
+we need a `templatetags` Python package
+inside of a Django application.
+We also need a module
+in that directory.
+Choose the module name carefully
+because it is what we will load
+in the template later on.
+
+```txt
+application
+├── templatetags
+│ ├── __init__.py
+│ └── custom_tags.py
+├── __init__.py
+├── ...
+├── models.py
+└── views.py
+```
+
+Next,
+we need to make our tag or filter
+and register it.
+Let's start with a filter example.
+
+```python
+# application/templatetags/custom_tags.py
+
+import random
+from django import template
+
+register = template.Library()
+
+@register.filter
+def add_pizzazz(value):
+ pieces_of_flair = [
+ ' Amazing!',
+ ' Wowza!',
+ ' Unbelievable!'
+ ]
+ return value + random.choice(pieces_of_flair)
+```
+
+Now,
+if we have a `message` variable,
+we can give it some pizzazz.
+To use the custom filter,
+we must load our tags module
+into the template
+with the `load` tag.
+
+{{< web >}}
+```django
+{% load custom_tags %}
+
+{{ message|add_pizzazz }}
+```
+{{< /web >}}
+{{< book >}}
+```djangotemplate
+{% load custom_tags %}
+
+{{ message|add_pizzazz }}
+```
+{{< /book >}}
+
+If our message was "You got a perfect score!",
+then our template should show the message
+and one of the three random choices
+like "You got a perfect score! Wowza!"
+
+Writing basic custom tags is very similar
+to custom filters.
+Code will speak better than words here.
+
+```python
+# application/templatetags/custom_tags.py
+
+import random
+from django import template
+
+register = template.Library()
+
+@register.simple_tag
+def champion_welcome(name, level):
+ if level > 42:
+ welcome = f"Hello great champion {name}!"
+ elif level > 20:
+ welcome = f"Greetings noble warrior {name}!"
+ elif level > 5:
+ welcome = f"Hello {name}."
+ else:
+ welcome = "Oh, it's you."
+ return welcome
+```
+
+We can load the custom tags and use our tag like any other built-in tag.
+
+{{< web >}}
+```django
+{% load custom_tags %}
+
+{% champion_welcome "He-Man" 50 %}
+```
+{{< /web >}}
+{{< book >}}
+```djangotemplate
+{% load custom_tags %}
+
+{% champion_welcome "He-Man" 50 %}
+```
+{{< /book >}}
+
+This silly welcome tag will respond
+to multiple input variables
+and vary depending on the provided level.
+The example usage should display "Hello great champion He-Man!"
+
+We're only looking at the most common kinds
+of custom tags
+in our examples.
+There are some more advanced custom tagging features
+which you can explore
+in the {{< extlink "https://docs.djangoproject.com/en/4.1/howto/custom-template-tags/" "Django custom template tags documentation" >}}.
+
+Django also uses `load`
+to provide template authors
+with some additional tools.
+For instance,
+we will see how to load some custom tags provided
+by the framework
+when we learn about working with images and JavaScript later on.
+
+## Summary
+
+Now we've seen templates in action!
+We've looked at:
+
+* How to set up templates for your site
+* Ways to call templates from views
+* How to use data
+* How to handle logic
+* Built-in tags and filters available to templates
+* Customizing templates with your own code extensions
+
+{{< web >}}
+In the next article,
+{{< /web >}}
+{{< book >}}
+In the next chapter,
+{{< /book >}}
+we are going to examine
+how users can send data to a Django application
+with HTML forms.
+Django has tools
+to make form building quick and effective.
+We're going to see:
+
+* The `Form` class that Django uses to handle form data in Python
+* Controlling what fields are in forms
+* How forms are rendered to users by Django
+* How to do form validation
+
+{{< web >}}
+If you'd like to follow along
+with the series,
+please feel free to sign up
+for my newsletter
+where I announce all of my new content.
+If you have other questions,
+you can reach me online
+on Twitter
+where I am
+{{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
+{{< /web >}}
+
diff --git a/content/understand-django/2020-05-05-user-interaction-forms.pt.md b/content/understand-django/2020-05-05-user-interaction-forms.pt.md
new file mode 100644
index 00000000..137e6ca9
--- /dev/null
+++ b/content/understand-django/2020-05-05-user-interaction-forms.pt.md
@@ -0,0 +1,887 @@
+---
+title: "User Interaction With Forms"
+description: >-
+ How do users provide data
+ to your website
+ so you can interact
+ with them?
+ We can answer that question
+ by exploring Django's form system,
+ and the tools that Django provides
+ to simplify your site
+ as you engage
+ with your users.
+image: img/django.png
+type: post
+categories:
+ - Python
+ - Django
+tags:
+ - Python
+ - Django
+ - forms
+
+---
+
+{{< web >}}
+In the previous
+[Understand Django]({{< ref "/understand-django/_index.md" >}})
+article,
+we saw how Django templates work
+to produce a user interface.
+That's fine
+if you only need to display a user interface,
+but what do you do
+{{< /web >}}
+{{< book >}}
+What do you do
+{{< /book >}}
+if you need your site
+to interact with users?
+You use Django's form system!
+{{< web >}}
+In this article,
+{{< /web >}}
+{{< book >}}
+In this chapter,
+{{< /book >}}
+we'll focus
+on how to work with web forms
+using the Django form system.
+
+{{< understand-django-series "forms" >}}
+
+## Web Forms 101
+
+Before we can dive into how Django handles forms,
+we need to have an understanding of HTML forms
+in general.
+Django's form functionality builds upon web forms
+so this topic won't make sense
+without a baseline knowledge
+of the topic.
+
+HTML can describe the type
+of data
+that you may want your users
+to send to your site.
+Collecting this data is done
+with a handful of tags.
+The primary HTML tags to consider are `form`, `input`, and `select`.
+
+A `form` tag is the container
+for all the data
+that you want a user to send
+to your application.
+The tag has two critical attributes
+that tell the browser how to send data: `action` and `method`.
+
+`action` would be better named as "destination" or "url."
+Alas, we are stuck with `action`.
+This attribute of the `form` tag is where user data should be sent to.
+It's also useful to know that leaving out `action`
+or using `action=""` will send any form data
+as an HTTP request to the same URL
+that the user's browser is on.
+
+The `method` attribute dictates which HTTP method to use
+and can have a value of `GET` or `POST`.
+When paired with `action`,
+the browser knows how to send a properly formatted HTTP request.
+
+Let's say we have this example.
+
+```html
+
+```
+
+When the form's method is `GET`,
+the form data will be sent as part
+of the URL
+in a querystring.
+The GET request sent to the server will look
+like `/some/form/?message=Hello`.
+This type of form submission is most useful
+when we don't need to save data
+and are trying to do some kind of query.
+For instance,
+you could give your application some search functionality
+with a URL like `/search/?q=thing+to+search`.
+These links could be bookmarked easily
+and are a natural fit
+for that kind of function.
+
+The `POST` method of sending form data is for data
+that we want to be secure or saved
+within an application.
+With a GET request,
+form data in the querystring is exposed
+in a number of places
+(see {{< extlink "https://owasp.org/www-community/vulnerabilities/Information_exposure_through_query_strings_in_url" "more information" >}}
+from the Open Web Application Security Project (OWASP)).
+On the other hand,
+POST sends the data in the body
+of the HTTP request.
+This means that if your site is secure
+(i.e., using HTTPS),
+then data is encrypted
+while traveling
+from a browser to a server.
+
+If you ever login to a website
+and submit a password
+in a form,
+you can be nearly certain
+that the form is sent
+with the POST method option
+(and if it's not, run away!).
+
+We've seen that `form` is the container
+that guides how to send form data.
+`input` and `select` are the tags
+that let us display a meaningful form
+to the user.
+
+The more prevalent tag is `input`.
+With the `input` tag,
+form authors will set `type` and `name` primarily.
+The `type` attribute tells the browser
+which kind of input to display.
+
+* Do we need a checkbox? `type="checkbox"`
+* Do we need a password field that hides characters? `type="password"`
+* How about a classic text box? `type="text"`
+
+You can see a full list of types
+on the {{< extlink "https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input" "MDN input documentation" >}} page.
+
+The other attribute, `name`, is the identifier
+that the form will pair with the user data.
+The server uses the identifier
+so it can distinguish
+between the pieces of data
+that a form submission may include.
+
+Another tag that your forms may use is the `select` tag.
+This kind of tag is less frequent
+than the `input` tag.
+The `select` tag lets users make a choice
+from a list of options.
+The default browser user interface
+for this tag is a dropdown menu.
+
+With these core elements
+of HTML forms,
+we are equipped
+to understand Django's form capabilities.
+Let's dive in!
+
+## Django Forms
+
+Django's form features act as a bridge
+between HTML forms
+and Python classes and data types.
+When presenting a form
+to a user via a view,
+the form system is able
+to display the proper HTML form tags
+and structure.
+When receiving this form data
+from a user's submission,
+the form system can translate the browser's raw form data
+into native Python data that we can use.
+
+We can begin
+with the `Form` class.
+A form class acts as the data declaration
+of what data we need
+from the user.
+Here's an example that we can examine.
+
+```python
+# application/forms.py
+
+from django import forms
+
+class ContactForm(forms.Form):
+ name = forms.CharField(
+ max_length=100
+ )
+ email = forms.EmailField()
+ message = forms.CharField(
+ max_length=1000
+ )
+```
+
+* User-defined Django forms should subclass the `Form` class.
+ This class adds a lot of powerful functionality
+ that will aid us
+ as we explore more.
+* The data that we want to collect is listed
+ as class level attributes.
+ Each field of the form is a certain field type
+ that has its own characteristics
+ to help translate raw form data
+ into the data types
+ that we want to work with in views.
+
+If we take this form
+and add it to a view's context as `form`,
+then we can render it in a template.
+The default rendering of the form uses an HTML table,
+but we can render the fields in a simpler format
+with the `as_p` method.
+This method will use paragraph tags instead
+for the form elements.
+If the template looks like:
+
+{{< web >}}
+```django
+{{ form.as_p }}
+```
+{{< /web >}}
+{{< book >}}
+```djangotemplate
+{{ form.as_p }}
+```
+{{< /book >}}
+
+Then Django will render:
+
+```html
+Name:
+
+Email:
+
+Message:
+
+
+```
+
+To make it possible to submit the form,
+we need to wrap this rendered output
+with a `form` tag
+and include a submit button
+and a CSRF token.
+
+Huh? *CSRF token?*
+Sadly,
+the world is full of nefarious people
+who would love to hack your application
+to steal data
+from others.
+A CSRF token is a security measure
+that Django includes to make it harder
+for malicious actors
+to tamper with your form's data.
+We'll talk more about security
+{{< web >}}
+in a future article.
+{{< /web >}}
+{{< book >}}
+in a future chapter.
+{{< /book >}}
+For now,
+sprinkle the token into your forms
+with Django's built-in template tag
+and everything should work.
+
+{{< web >}}
+```django
+
+```
+{{< /web >}}
+{{< book >}}
+```djangotemplate
+
+```
+{{< /book >}}
+
+That's how a form gets displayed.
+Now let's look at a view that handles the form properly.
+When working with form views,
+we will often use a view
+that is able to handle
+both `GET` and `POST` HTTP requests.
+Here's a full view
+that we can break down piece by piece.
+The example uses a function view
+for simplicity,
+but you could do something similar
+with a class-based view.
+
+```python
+# application/views.py
+
+from django.http import HttpResponseRedirect
+from django.shortcuts import render
+from django.urls import reverse
+
+from .forms import ContactForm
+
+def contact_us(request):
+ if request.method == "POST":
+ form = ContactForm(request.POST)
+ if form.is_valid():
+ # Do something with the form data
+ # like send an email.
+ return HttpResponseRedirect(
+ reverse('some-form-success-url')
+ )
+ else:
+ form = ContactForm()
+
+ return render(
+ request,
+ 'contact_form.html',
+ {'form': form}
+ )
+```
+
+If we start
+by thinking about the `else` branch,
+we can see how little this view does
+on a `GET` request.
+When the HTTP method is a `GET`,
+it creates an empty form
+with no data passed to the constructor
+and renders a template
+with the `form`
+in the context.
+
+`contact_form.html` contains the Django template above
+to display the HTML form
+to the user.
+When the user clicks "Send the form!",
+another request comes
+to the same view,
+but this time the method is an HTTP `POST`
+and contains the submitted data.
+
+The `POST` request creates a `form`,
+but there is a difference
+in how it is constructed.
+The form submission data is stored
+in `request.POST`,
+which is a dictionary-like object
+that we first encountered
+{{< web >}}
+in the views article.
+{{< /web >}}
+{{< book >}}
+in the views chapter.
+{{< /book >}}
+By passing `request.POST`
+to the form's constructor,
+we create a form with data.
+In the Django documentation,
+you will see this called a *bound form*
+because data is *bound* to the form.
+
+With the form ready,
+the view checks if the data is valid.
+We'll talk about form validation
+in detail later
+{{< web >}}
+in this article.
+{{< /web >}}
+{{< book >}}
+in this chapter.
+{{< /book >}}
+In this instance,
+you can see that `is_valid` could return `False`
+if the form data contained "I am not an email address"
+in the `email` field, for instance.
+
+* When the form is valid,
+ the view does the extra work represented
+ by the comment
+ and redirects to a new view
+ that can show some kind of success message.
+* When the form is invalid,
+ the view goes out of the `if` clause
+ and calls `render`.
+ Since the data is bound to the `form`,
+ the contact form has enough information
+ to show which form fields
+ caused the errors
+ that made the form invalid.
+
+That's the core of form handling!
+The presented view is a common pattern
+for handling form views
+in Django.
+In fact, this view pattern is so common
+that Django provides a built-in view
+to implement what is done in the example
+named `FormView`.
+
+```python
+# application/views.py
+
+from django.views.generic import FormView
+from django.urls import reverse
+
+from .forms import ContactForm
+
+class ContactUs(FormView):
+ form_class = ContactForm
+ template_name = 'contact_form.html'
+
+ def get_success_url(self):
+ return reverse(
+ 'some-form-success-view'
+ )
+
+ def form_valid(self, form):
+ # Do something with the form data
+ # like send an email.
+ return super().form_valid(form)
+```
+
+The `FormView` expects a form class and template name
+and provides some methods to override
+for the common places
+where your own application logic should live.
+
+## Form Fields
+
+With the basics of form handling done,
+we can turn our attention
+to the kinds
+of fields
+that forms can use.
+The extensive list of fields is
+in {{< extlink "https://docs.djangoproject.com/en/4.1/ref/forms/fields/" "the Django documentation" >}},
+and we will look at a few
+of the most common ones
+{{< web >}}
+in this article.
+{{< /web >}}
+{{< book >}}
+in this chapter.
+{{< /book >}}
+
+The first thing to remember about Django form fields is
+that they convert HTML form data
+into native Python data types.
+If you examine the data
+of a form submission,
+you'll discover
+that each value is essentially a string
+by default.
+If Django did nothing for you,
+then you would constantly have to convert
+to the data types that you want.
+By working with form fields,
+that data conversion is automatically handled
+for you.
+For instance,
+if you choose a `BooleanField`,
+after the Django form is validated,
+that field value will be either `True` or `False`.
+
+Another important item to know about fields is
+that they are associated
+with particular Django widgets.
+Widgets are the way
+to control what Django renders
+when you render a form.
+Each form field has a default widget type.
+Sticking with `BooleanField`,
+its default widget is a `CheckboxInput`
+which will render an `input` tag
+with a type of `checkbox`
+(i.e., your standard form checkbox).
+
+Fields are the critical intersection
+between the world of the browser and HTML
+and the Python world
+with all of its robust data types.
+
+What fields are you most likely
+to reach for?
+And what do you need to set on those fields?
+
+### CharField
+
+`CharField` is a real workhorse
+for Django forms.
+The `CharField` captures text input
+and uses a standard `input` tag
+with a type of `text`.
+If you want to collect more text,
+like in a feedback form,
+you can switch
+from the default `TextInput` widget
+to a `Textarea` widget.
+This will make your form render a `textarea` tag
+that will give far more space
+for any input.
+
+```python
+# application/forms.py
+
+from django import forms
+
+class FeedbackForm(forms.Form):
+ email = forms.EmailField()
+ comment = forms.CharField(
+ widget=forms.Textarea
+ )
+```
+
+### EmailField
+
+The `EmailField` is like a specialized version
+of the `CharField`.
+The field uses an `input` tag
+with a type of `email`.
+Many modern browsers can help
+to check that valid email addresses are provided.
+Also, when this field is validated within the framework,
+Django will attempt to validate the email address too
+in case the browser wasn't able to do it.
+
+### DateField
+
+A `DateField` is another field
+that is mostly like a `CharField`.
+The field even uses the `input` type of `text`
+when rendered.
+The difference with this field comes
+from the data type that the form will provide
+after it is validated.
+A `DateField` will convert
+{{< extlink "https://docs.djangoproject.com/en/4.1/ref/settings/#datetime-input-formats" "a variety of string formats" >}}
+into a Python `datetime.date` object.
+
+### ChoiceField
+
+A `ChoiceField` is useful
+when you want a user to make a choice
+from a list of options.
+For this field type,
+we must provide a list of choices
+that the user can pick from.
+Imagine that we want to ask users
+what their favorite meal of the day is.
+Here's a form that can do that.
+
+```python
+# application/forms.py
+
+from django import forms
+
+class SurveyForm(forms.Form):
+ MEALS = [
+ ("b", "Breakfast"),
+ ("l", "Lunch"),
+ ("d", "Dinner")
+ ]
+ favorite_meal = forms.ChoiceField(
+ choices=MEALS
+ )
+```
+
+This will contain a form
+with a `select` tag
+that looks like:
+
+```html
+
+ Favorite meal:
+
+ Breakfast
+ Lunch
+ Dinner
+
+
+```
+
+This handful of fields will deal
+with most form needs.
+Be sure to explore the full list
+of what is available
+to equip yourself
+with other beneficial types.
+
+Form fields share some common attributes
+for things that each field needs.
+
+The `required` attribute is a boolean
+that specifies whether a field must have a value or not.
+For instance,
+it wouldn't make much sense
+if your site had a support form
+that you planned to use to contact people via email,
+and an `email` field
+on the form
+was optional.
+
+`label` sets what text is used
+for the `label` tag
+that is rendered
+with a form `input`.
+In the meal example,
+we could use the `label` attribute to change "Favorite meal"
+into "What is your favorite meal?"
+That would make a much better survey experience.
+
+Sometimes forms may not be clear
+and users need help.
+You can add a `help_text` attribute
+that will render additional text
+by your form field wrapped in a `span` tag
+with a `helptext` class
+if you want to style it with CSS.
+
+Because forms are one of the main ways
+that users will provide information
+to your application,
+the forms system is rich with features.
+You can learn a lot about Django
+by diving deeply
+into that portion
+of the documentation.
+
+Let's shift our focus
+to form validation
+since I've mentioned it a few times
+in passing now.
+
+## Validating Forms
+
+In the view example,
+I showed a form
+that calls the `is_valid` method.
+At a high level,
+we can understand
+what that method is doing;
+it's determining whether the form is valid or not.
+
+But what is `is_valid` actually doing?
+It does a lot!
+
+The method handles each of the fields.
+As we saw,
+fields can have a final data type
+(like with `BooleanField`)
+or an expected structure
+(like with `EmailField`).
+This process of converting data types
+and validating the field data is called cleaning.
+In fact,
+each field must have a `clean` method
+that the form will call
+when `is_valid` is called.
+
+When `is_valid` is `True`,
+the form's data will be
+in a dictionary named `cleaned_data`
+with keys
+that match the field names
+declared by the form.
+With the validated data,
+you access `cleaned_data` to do your work.
+For instance,
+if we had some kind of integration
+with a support ticket system,
+perhaps our `FeedbackForm` above is handled
+in the view like:
+
+```python
+if form.is_valid():
+ email = form.cleaned_data['email']
+ comment = form.cleaned_data['comment']
+ create_support_ticket(
+ email,
+ comment
+ )
+ return HttpReponseRedirect(
+ reverse('feedback-received')
+ )
+```
+
+When `is_valid` is `False`,
+Django will store the errors it found
+in an `errors` attribute.
+This attribute will be used
+when the form is re-rendered
+on the page
+(because, if you recall from the view example,
+the form view pattern sends a bound form
+back through a `render` call
+in the failure case).
+
+Once again,
+Django is doing a lot of heavy lifting
+for you
+to make working with forms easier.
+The system *also* permits developers
+to add custom validation logic.
+
+If you have a form field,
+you can add customization
+by writing a method on the form class.
+The format of the method must match with the field name
+and prepend `clean_`.
+Let's imagine that we want a website
+for Bobs.
+In order to sign up for the website,
+your email address must have "bob"
+in it.
+We can write a clean method
+to check for that.
+
+```python
+# application/forms.py
+
+from django import forms
+
+class SignUpForm(forms.Form):
+ email = forms.EmailField()
+ password = forms.CharField(
+ widget=forms.PasswordInput
+ )
+
+ def clean_email(self):
+ email = self.cleaned_data['email']
+ if 'bob' not in email:
+ raise forms.ValidationError(
+ 'Sorry, you are not a Bob.'
+ )
+ return email
+```
+
+There are a few important points about this:
+
+* `clean_email` will only try to clean the `email` field.
+* If validation fails,
+ your code should raise a `ValidationError`.
+ Django will handle that and put the error
+ in the right format
+ in the `errors` attribute
+ of the form.
+* If everything is good,
+ be sure to return the cleaned data.
+ That is part of the interface
+ that Django expects for clean methods.
+
+These `clean_` methods are hooks
+that let you include extra checking.
+This hook system gives you the perfect place
+to put validation logic for data
+that is specific
+to your application.
+But what about validating multiple pieces
+of data?
+This might happen when data has some kind
+of interrelationship.
+For instance,
+if you're putting together a genealogy website,
+you may have a form that records birth and death dates.
+You might want to check those dates.
+
+```python
+# application/forms.py
+
+from django import forms
+
+class HistoricalPersonForm(forms.Form):
+ name = forms.CharField()
+ date_of_birth = forms.DateField()
+ date_of_death = forms.DateField()
+
+ def clean(self):
+ cleaned_data = super().clean()
+ date_of_birth = cleaned_data.get('date_of_birth')
+ date_of_death = cleaned_data.get('date_of_death')
+ if (
+ date_of_birth and
+ date_of_death and
+ date_of_birth > date_of_death
+ ):
+ raise forms.ValidationError(
+ 'Birth date must be before death date.'
+ )
+ return cleaned_data
+```
+
+This method is similar to `clean_`,
+but we must be more careful.
+Individual field validations run first,
+but they may have failed!
+When clean methods fail,
+the form field is removed from `cleaned_data`
+so we can't do a direct key access.
+The clean method checks if the two dates are truthy and each has a value,
+then does the comparison between them.
+
+Custom validation is a great feature to improve the quality
+of the data you collect
+from users of your application.
+
+## Summary
+
+That's how forms make it possible
+to collect data
+from your users
+so your site can interact with them.
+We've seen:
+
+* Web forms and the `form` HTML tag
+* The `Form` class that Django uses to handle form data in Python
+* How forms are rendered to users by Django
+* Controlling what fields are in forms
+* How to do form validation
+
+Now that we know how to collect data
+from users,
+how can we make the application hold onto that data
+for them?
+{{< web >}}
+In the next article,
+{{< /web >}}
+{{< book >}}
+In the next chapter,
+{{< /book >}}
+we will begin to store data
+in a database.
+We'll work with:
+
+* How to set up a database for your project.
+* How Django uses special classes called models to keep data.
+* Running the commands that will prepare a database
+ for the models you want to use.
+* Saving new information into the database.
+* Asking the database for information that we stored.
+
+{{< web >}}
+If you'd like to follow along
+with the series,
+please feel free to sign up
+for my newsletter
+where I announce all of my new content.
+If you have other questions,
+you can reach me online
+on Twitter
+where I am
+{{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
+{{< /web >}}
+
diff --git a/content/understand-django/2020-06-25-store-data-with-models.pt.md b/content/understand-django/2020-06-25-store-data-with-models.pt.md
new file mode 100644
index 00000000..de4afbbf
--- /dev/null
+++ b/content/understand-django/2020-06-25-store-data-with-models.pt.md
@@ -0,0 +1,1449 @@
+---
+title: "Store Data With Models"
+description: >-
+ In this article,
+ we will see how to store data
+ into a database
+ with Django models.
+ The article covers how models act
+ as an interface
+ to let your application
+ store and fetch data.
+image: img/django.png
+type: post
+categories:
+ - Python
+ - Django
+tags:
+ - Python
+ - Django
+ - models
+
+---
+
+{{< web >}}
+In the previous
+[Understand Django]({{< ref "/understand-django/_index.md" >}})
+article,
+we encountered forms
+and how forms allow your application
+to receive data
+from users
+who use your site.
+In this article,
+you'll see how to take
+that data
+{{< /web >}}
+{{< book >}}
+In this chapter,
+you'll see how to take data
+{{< /book >}}
+and store it
+into a database
+so that your application can use that data
+or display it later.
+
+{{< understand-django-series "models" >}}
+
+## Setting Up
+
+Let's figure out where your data goes
+before getting deep
+into how to work with it.
+Django uses databases
+to store data.
+More specifically,
+Django uses *relational* databases.
+Covering relational databases would be a gigantic topic
+so you'll have to settle
+for a **very** abridged version.
+
+A relational database is like a collection
+of spreadsheets.
+Each spreadsheet is actually called a table.
+A table has a set of columns
+to track different pieces
+of data.
+Each row in the table would represent a related group.
+For instance,
+imagine we have an employee table
+for a company.
+The columns for an employee table might include a first name, last name,
+and job title.
+Each row would represent an individual employee.
+
+```text
+First name | Last name | Job title
+-----------|-----------|----------
+John | Smith | Software Engineer
+-----------|-----------|----------
+Peggy | Jones | Software Engineer
+```
+
+The "relational" part of a relational database comes into play
+because multiple tables can *relate*
+to each other.
+In our company example,
+the database could have a table of phone numbers
+that it uses to store the phone number
+of each employee.
+
+Why not put the phone number in the same employee table?
+Well,
+what would happen if a company needed a cell phone number
+and home phone number?
+By having separate tables,
+we could support tracking multiple phone number types.
+There is a lot of power that comes
+from being able to separate these different kinds
+of data.
+We'll see the power of relational databases
+as we explore how Django exposes
+that power.
+
+Django uses a relational database
+so the framework must have some ability
+to set up that database.
+The database configuration is in the `DATABASES` setting
+in your `settings.py` file.
+After running `startproject`,
+you'll find:
+
+```python
+DATABASES = {
+ 'default': {
+ 'ENGINE': 'django.db.backends.sqlite3',
+ 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
+ }
+}
+```
+
+Like we saw with the templates system,
+Django supports multiple databases.
+Unlike the template system,
+the database settings refer to each supported backend as an "engine"
+instead of "backend."
+The default database engine
+from `startproject`
+is set to use
+{{< extlink "https://www.sqlite.org/index.html" "SQLite" >}}.
+SQLite is a great starting choice
+because it fits an entire relational database
+into a single file
+which the settings name `db.sqlite3`.
+This choice of engine really lowers the barrier
+to starting
+with Django
+since new Django developers don't have to go download additional tools
+to try Django out.
+
+SQLite is an amazing little database
+and probably the mostly widely used database
+in the world.
+The database exists in every smartphone you could think of.
+Even though SQLite is amazing,
+it's not a good fit for many scenarios
+where you'd want to use Django.
+For starters,
+the database only permits one user
+to write to it
+at a time.
+That's a huge problem if you're planning
+to make a site that serves many people simultaneously.
+
+Because SQLite is not the best fit
+for an application on the web,
+you'll likely need to switch
+to a different relational database.
+I'd recommend {{< extlink "https://www.postgresql.org/" "PostgreSQL" >}}.
+Postgres (as it is often "abbreviated" to) is a wildly popular,
+open source database
+that is very well supported.
+Combined with {{< extlink "https://www.psycopg.org/docs/" "psycopg2" >}}
+as the Django engine,
+you'll find that many places
+that can host your Django app will work well
+with Postgres.
+
+We can explore more database configuration
+{{< web >}}
+in a future article
+{{< /web >}}
+{{< book >}}
+in a future chapter
+{{< /book >}}
+on deployment.
+For now,
+while you're learning,
+SQLite is perfectly well suited
+for the task.
+
+## Modeling Your Data
+
+Now that you have an idea
+of where Django will store your data,
+let's focus on *how* Django will store data.
+
+Django represents data
+for a database
+in Python classes
+called **models**.
+Django models are similar
+to the form classes
+that we saw
+{{< web >}}
+in the last article.
+{{< /web >}}
+{{< book >}}
+in the last chapter.
+{{< /book >}}
+A Django model declares the data
+that you want to store
+in the database
+as class level attributes,
+just like a form class.
+In fact,
+the types of fields are extremely similar
+to their form counterparts,
+and for good reason!
+We often want to save form data
+and store it
+so it makes sense for models
+to be similar to forms
+in structure.
+Let's look at an example.
+
+```python
+# application/models.py
+from django.db import models
+
+class Employee(models.Model):
+ first_name = models.CharField(
+ max_length=100
+ )
+ last_name = models.CharField(
+ max_length=100
+ )
+ job_title = models.CharField(
+ max_length=200
+ )
+```
+
+This model class describes the pieces
+that we want to include
+in a database table.
+Each model class represents one database table.
+If we wanted the phone numbers
+that I mentioned earlier,
+we'd create a separate `PhoneNumber` class.
+Conventionally,
+we use a singular name
+instead of a plural
+when naming the class.
+We do that because each *row*
+in the table
+is represented
+as an object instance.
+
+```python
+>>> from application.models import Employee
+>>> employee = Employee(
+... first_name='Tom',
+... last_name='Bombadil',
+... job_title='Old Forest keeper')
+>>> employee.first_name
+'Tom'
+```
+
+This example appears
+to create a new employee,
+but it's missing a key element.
+We haven't saved the `employee` instance
+to the database.
+We can do that with `employee.save()`,
+but,
+if you are following along
+and try to call that right now,
+it will fail
+with an error that says
+that the employee table doesn't exist.
+
+Since the database is a tool
+that is external to Django,
+the database needs a bit of preparation
+before it can receive data
+from Django.
+
+## Preparing A Database With Migrations
+
+We now know that Django models are Python classes
+that map to database tables.
+Database tables don't magically appear.
+We need the ability to set up tables
+so that they will match the structure
+defined in the Python class.
+The tool Django provides
+to make Django models
+and a database sync up
+is called the migrations system.
+
+Migrations are Python files
+that describe the sequence
+of database operations
+that are needed
+to make a database match any model definitions
+that you have in your project.
+
+Because Django works
+with many databases,
+these database operations are defined
+in Python files
+so that the operations can be abstract.
+By using abstract operations,
+the Django migration system can plug in the specific database commands
+for whatever database you're using.
+If you're starting out with SQLite,
+then moving to PostgreSQL
+when you're ready
+to put your application
+on the internet,
+then the migration system will do its best
+to smooth over the differences
+to minimize the amount
+of work you would need
+while making the transition.
+
+Initially,
+you can get pretty far
+without understanding the internals
+of how migration files work.
+At the core level,
+you need to learn a couple
+of Django commands:
+`makemigrations`
+and `migrate`.
+
+### `makemigrations`
+
+The `makemigrations` command will create any migration files
+if there are any pending model changes.
+To create our migration file
+for the `Employee` model,
+we can run:
+
+```bash
+(venv) $ ./manage.py makemigrations
+Migrations for 'application':
+ application/migrations/0001_initial.py
+ - Create model Employee
+```
+
+The important thing to note is that we require a new migration
+when we make model changes
+that update any model fields.
+This includes:
+
+* Adding new models or new fields
+* Modifying existing fields
+* Deleting existing fields
+* Changing some model metadata and a few other edge cases
+
+If you do not make a migration,
+you will likely encounter errors
+when fetching data
+from the database.
+This is because Django only builds queries
+based on what is defined
+in the Python code.
+The system assumes
+that the database is in the proper state.
+Django will try to query
+on database tables
+even if those tables don't exist yet!
+
+### `migrate`
+
+The other command, `migrate`,
+takes migration files
+and applies them
+to a database.
+For example:
+
+```bash
+(venv) $ ./manage.py migrate
+Operations to perform:
+ Apply all migrations: admin, application, auth, contenttypes, sessions
+Running migrations:
+ Applying contenttypes.0001_initial... OK
+ Applying auth.0001_initial... OK
+ Applying admin.0001_initial... OK
+ Applying admin.0002_logentry_remove_auto_add... OK
+ Applying admin.0003_logentry_add_action_flag_choices... OK
+ Applying application.0001_initial... OK
+ Applying contenttypes.0002_remove_content_type_name... OK
+ Applying auth.0002_alter_permission_name_max_length... OK
+ Applying auth.0003_alter_user_email_max_length... OK
+ Applying auth.0004_alter_user_username_opts... OK
+ Applying auth.0005_alter_user_last_login_null... OK
+ Applying auth.0006_require_contenttypes_0002... OK
+ Applying auth.0007_alter_validators_add_error_messages... OK
+ Applying auth.0008_alter_user_username_max_length... OK
+ Applying auth.0009_alter_user_last_name_max_length... OK
+ Applying auth.0010_alter_group_name_max_length... OK
+ Applying auth.0011_update_proxy_permissions... OK
+ Applying sessions.0001_initial... OK
+```
+
+This is the output that happens
+when applying our new migration.
+What is all this stuff!?
+
+The migration system is also used
+by built-in Django applications.
+In my sample project,
+I used `startproject`
+which includes a set
+of common applications
+in the `INSTALLED_APPS` list.
+We can observe that our sample `application` applied its migration,
+and the migrations
+from the other Django applications
+that we included are also applied.
+
+If you run the `migrate` command again,
+you won't see the same output.
+That's because Django keeps tracks
+of which migrations were applied.
+The migration system will only execute any *unapplied* migrations.
+
+You can also limit which migrations to execute
+by providing a Django app name.
+
+```bash
+(venv) $ ./manage.py migrate application
+```
+
+Those are the fundamentals
+of migrations.
+You can also use migrations
+to apply more complex operations
+like actions
+that are specific
+to your selected database.
+You can learn more
+about what Django migrations can do
+in the {{< extlink "https://docs.djangoproject.com/en/4.1/topics/migrations/" "Migrations documentation" >}}.
+
+## Working With Models
+
+After running migrations,
+your database will be prepared
+to communicate properly
+with Django.
+
+To create new rows
+in our new database tables,
+we can use a model's `save` method.
+When you save a model instance,
+Django will send a message
+to the database
+that effectively says
+"add this new data
+to this database table."
+These database "messages"
+are actually called **queries**.
+
+As I mentioned
+in the settings section,
+Django communicates
+with the database
+via a database engine.
+The database engine uses Structured Query Language (SQL)
+to communicate
+with the actual database.
+SQL is the common standard
+that Django's supported databases all "speak."
+Since Django uses SQL,
+that's why a message is named "query."
+
+What does an SQL query look like?
+If we save our model example from earlier,
+it would look something like:
+
+```sql
+INSERT INTO "application_employee"
+ ("first_name", "last_name", "job_title")
+VALUES
+ ('Tom', 'Bombadil', 'Old Forest keeper')
+```
+
+Note that this example is an `INSERT` query
+when using the SQLite engine.
+Like natural languages,
+SQL has a variety of dialects
+depending on the database
+that you select.
+The databases do their best to adhere
+to some standards,
+but each database has its quirks,
+and the job
+of the database engine is
+to even out those differences
+where possible.
+
+This is another area where Django does a ton
+of hard work
+on your behalf.
+The database engine translates your `save` call
+into the proper SQL query.
+SQL is a deep topic
+that we can't possibly cover fully
+{{< web >}}
+in this article.
+{{< /web >}}
+{{< book >}}
+in this chapter.
+{{< /book >}}
+Thankfully,
+we don't have to
+because of Django's ORM!
+
+ORM stands for Object Relational Mapper.
+The job of an ORM
+is to map (or translate)
+from Python *objects*
+to a *relational* database.
+This means that we spend our time working
+in Python code,
+and we let Django figure out how
+to get and put data
+into the database.
+
+Using `save` on a model record is such a small example
+of the Django ORM.
+What else can we do?
+We can do things like:
+
+* Get all rows from the database.
+* Get a filtered set of rows based on some filtering criteria.
+* Update a set of rows at the same time.
+* Delete rows from the database.
+
+Most of these operations
+with the Django ORM work
+through a `Manager` class.
+Where our previous example showed how
+to manipulate a single row,
+a model manager has methods designed
+for interacting with multiple rows.
+
+We can analyze our fictitious employee table.
+The manager for a model is attached to the model class
+as an attribute named `objects`.
+Let's see some code:
+
+```python
+>>> from application.models import Employee
+>>> bobs = Employee.objects.filter(first_name='Bob')
+>>> for bob in bobs:
+... print(f"{bob.first_name} {bob.last_name}")
+...
+Bob Ross
+Bob Barker
+Bob Marley
+Bob Dylan
+>>> print(bobs.query)
+SELECT
+ "application_employee"."id",
+ "application_employee"."first_name",
+ "application_employee"."last_name",
+ "application_employee"."job_title"
+FROM "application_employee"
+WHERE "application_employee"."first_name" = Bob
+```
+
+In this example,
+we're using the manager
+to filter to a subset
+of employees
+within the table.
+The `bobs` variable returned
+by the `filter` method
+is a `QuerySet`.
+As you might guess,
+it represents a set of rows
+that an SQL query will return.
+Whenever you have a queryset,
+you can print the query
+to see the exact SQL statement
+that Django will run
+on your behalf.
+
+What if you want to delete an employee record?
+
+```python
+>>> from application.models import Employee
+>>> # The price is wrong, Bob!
+>>> Employee.objects.filter(
+... first_name='Bob',
+... last_name='Barker').delete()
+(1, {'application.Employee': 1})
+```
+
+A queryset can apply operations in bulk.
+In this case,
+the filter is sufficiently narrow
+that only one record was deleted,
+but it could have included more
+if the SQL query matched more database table rows.
+
+The `QuerySet` class has a variety
+of methods
+that are useful
+when working with tables.
+Some of the methods also have the interesting property
+of returning a new queryset.
+This is a beneficial capability
+when you need to apply additional logic
+for your query.
+
+```python
+from application.models import Employee
+
+# employees is a QuerySet of all rows!
+employees = Employee.objects.all()
+
+if should_find_the_bobs:
+ # New queryset!
+ employees = employees.filter(
+ first_name='Bob'
+ )
+```
+
+Here are some other `QuerySet` methods
+that I use constantly:
+
+* `create` - As an alternative to creating a record instance
+ and calling `save`,
+ the manager can create a record directly.
+
+```python
+Employee.objects.create(
+ first_name='Bobby',
+ last_name='Tables'
+)
+```
+
+* `get` - Use this method when you want one
+ *and exactly one* record.
+ If your query doesn't match
+ or will return multiple records,
+ you'll get an exception.
+
+```python
+the_bob = Employee.objects.get(
+ first_name='Bob',
+ last_name='Marley'
+)
+
+Employee.objects.get(first_name='Bob')
+# Raises
+# application.models.Employee.MultipleObjectsReturned
+
+Employee.objects.get(
+ first_name='Bob',
+ last_name='Sagat'
+)
+# Raises application.models.Employee.DoesNotExist
+```
+
+* `exclude` - This method lets you exclude rows
+ that may be part of your existing queryset.
+
+```python
+the_other_bobs = (
+ Employee.objects.filter(first_name='Bob')
+ .exclude(last_name='Ross')
+)
+```
+
+* `update` - With this method,
+ you can update a group of rows
+ in a single operation.
+
+```python
+Employee.objects.filter(
+ first_name='Bob'
+).update(first_name='Robert')
+```
+
+* `exists` - Use this method if you want to check
+ if rows exist in the database
+ that match the condition you want to check.
+
+```python
+has_bobs = Employee.objects.filter(
+ first_name='Bob').exists()
+```
+
+* `count` - Check how many rows match a condition.
+ Because of how SQL works,
+ note that this is more efficient
+ than trying to use `len` on a queryset.
+
+```python
+how_many_bobs = Employee.objects.filter(
+ first_name='Bob').count()
+```
+
+* `none` - This returns an empty queryset for the model.
+ How could this be useful?
+ I use this when I need to protect certain data access.
+
+```python
+employees = Employee.objects.all()
+
+if not is_hr:
+ employees = Employee.objects.none()
+```
+
+* `first` / `last` - These methods will return an individual model instance
+ if one matches.
+ The methods use ordering on the models
+ to get the desired result.
+ We use `order_by` to tell how we want the results arranged.
+
+```python
+>>> a_bob = Employee.objects.filter(
+... first_name='Bob').order_by(
+... 'last_name').last()
+>>> print(a_bob.last_name)
+Ross
+```
+
+An `order_by` operation can also reverse the order of the results.
+To do this, add a dash before the field name
+like `order_by('-last_name')`.
+
+With the knowledge
+of how you can interact
+with models,
+we can focus more closely
+on what data can be stored
+in models
+(and, thus, in your database).
+
+## Types Of Model Data
+
+The `Employee` table that I've used
+as the example
+{{< web >}}
+for this article
+{{< /web >}}
+{{< book >}}
+for this chapter
+{{< /book >}}
+only has three `CharField` fields
+on the model.
+The choice was deliberate
+because I wanted you
+to have a chance
+to absorb a bit about the Django ORM
+and working with querysets
+before seeing other data types.
+
+{{< web >}}
+We saw in the forms article
+{{< /web >}}
+{{< book >}}
+We saw in the forms chapter
+{{< /book >}}
+that Django's form system
+includes a wide variety
+of form fields.
+If you look at the
+{{< extlink "https://docs.djangoproject.com/en/4.1/ref/forms/fields/" "Form fields" >}} reference
+and compare the list
+of types
+to those in the
+{{< extlink "https://docs.djangoproject.com/en/4.1/ref/models/fields/" "Model field reference" >}},
+you can observe a lot of overlap.
+
+Like their form counterparts,
+models have `CharField`,
+`BooleanField`,
+`DateField`,
+`DateTimeField`,
+and many other similar types.
+The field types share many common attributes.
+Most commonly,
+I think you will use or encounter the following attributes.
+
+* `default` - If you want to be able to create a model record
+ without specifying certain values,
+ then you can use `default`.
+ The value can either be a literal value
+ or callable function that produces a value.
+
+```python
+# application/models.py
+import random
+
+from django.db import models
+
+def strength_generator():
+ return random.randint(1, 20)
+
+class DungeonsAndDragonsCharacter(
+ models.Model
+):
+ name = models.CharField(
+ max_length=100,
+ default='Conan'
+ )
+ # Important: Pass the function,
+ # do not *call* the function!
+ strength = models.IntegerField(
+ default=strength_generator
+ )
+```
+
+* `unique` - When a field value must be unique
+ for all the rows in the database table,
+ use `unique`.
+ This is a good attribute
+ for identifiers
+ where you don't expect duplicates.
+
+```python
+class ImprobableHero(models.Model):
+ name = models.CharField(
+ max_length=100,
+ unique=True
+ )
+
+# There can be only one.
+ImprobableHero.objects.create(
+ name='Connor MacLeod'
+)
+```
+
+* `null` - A relational database has the ability
+ to store the absence of data.
+ In the database,
+ this value is thought of as `NULL`.
+ Sometimes this is an important distinction
+ versus a value that is empty.
+ For instance,
+ on a `Person` model,
+ an integer field like `number_of_children` would mean very different things
+ for a value of 0
+ versus a value of `NULL`.
+ The former indicates that a person has no children
+ while the latter indicates
+ that the number of children is unknown.
+ The presence of null conditions requires more checking
+ in your code
+ so Django defaults to making `null` be `False`.
+ This means that a field does not allow `NULL`.
+ Null values can be useful if needed,
+ but I think its better to avoid them
+ if you can
+ and try to keep actual data about a field.
+
+```python
+class Person(models.Model):
+ # This field would always have a value since it can't be null.
+ # Zero counts as a value and is not NULL.
+ age = models.IntegerField()
+ # This field could be unknown and contain NULL.
+ # In Python, a NULL db value will appear as None.
+ weight = models.IntegerField(
+ null=True
+ )
+```
+
+{{< web >}}
+* `blank` - The `blank` attribute is often used
+ in conjunction with the `null` attribute.
+ While the `null` attribute allows a database
+ to store `NULL`
+ for a field,
+ `blank` allows *form validation*
+ to permit an empty field.
+ This is used by forms
+ which are automatically generated
+ by Django
+ like in the Django administrator site
+ which we'll talk about in the next article.
+{{< /web >}}
+{{< book >}}
+* `blank` - The `blank` attribute is often used
+ in conjunction with the `null` attribute.
+ While the `null` attribute allows a database
+ to store `NULL`
+ for a field,
+ `blank` allows *form validation*
+ to permit an empty field.
+ This is used by forms
+ which are automatically generated
+ by Django
+ like in the Django administrator site
+ which we'll talk about in the next chapter.
+{{< /book >}}
+
+```python
+class Pet(models.Model):
+ # Not all pets have tails,
+ # so we want auto-generated forms
+ # to allow no value.
+ length_of_tail = models.IntegerField(
+ null=True,
+ blank=True
+ )
+```
+
+{{< web >}}
+* `choices` - We saw `choices`
+ in the forms article
+ as a technique
+ for helping users pick the right value
+ from a constrained set.
+ `choices` can be set on the model.
+ Django can do validation
+ on the model
+ that will ensure
+ that only particular values are stored
+ in a database field.
+{{< /web >}}
+{{< book >}}
+* `choices` - We saw `choices`
+ in the forms chapter
+ as a technique
+ for helping users pick the right value
+ from a constrained set.
+ `choices` can be set on the model.
+ Django can do validation
+ on the model
+ that will ensure
+ that only particular values are stored
+ in a database field.
+{{< /book >}}
+
+```python
+class Car(models.Model):
+ COLOR_CHOICES = [
+ (1, 'Black'),
+ (2, 'Red'),
+ (3, 'Blue'),
+ (4, 'Green'),
+ (5, 'White'),
+ ]
+ color = models.IntegerField(
+ choices=COLOR_CHOICES,
+ default=1
+ )
+```
+
+* `help_text` - As applications get bigger
+ or if you work on a large team
+ with many people creating Django models,
+ the need for documentation grows.
+ Django permits help text
+ that can be displayed
+ with a field value
+ in the Django administrator site.
+ This help text is useful
+ to remind your future self
+ or educate a coworker.
+
+```python
+class Policy(models.Model):
+ is_section_987_123_compliant = models.BooleanField(
+ default=False,
+ help_text=(
+ 'For policies that only apply'
+ ' on leap days in accordance'
+ ' with Section 987.123'
+ ' of the Silly Draconian Order'
+ )
+ )
+```
+
+Those are the attributes
+that I believe users are most likely
+to encounter.
+There are also a couple of important field types
+that require special attention:
+relational fields.
+
+## What Makes A Database "Relational?"
+
+Relational databases have the ability
+to link different types
+of data together.
+We got a brief example
+of this earlier
+{{< web >}}
+in this article
+{{< /web >}}
+{{< book >}}
+in this chapter
+{{< /book >}}
+when we considered
+an employee
+with multiple phone numbers.
+
+An overly simplified model
+for an employee
+with multiple phone numbers might look like:
+
+```python
+# application/models.py
+from django.db import models
+
+class Employee(models.Model):
+ first_name = models.CharField(
+ max_length=100
+ )
+ last_name = models.CharField(
+ max_length=100
+ )
+ job_title = models.CharField(
+ max_length=200
+ )
+ phone_number_1 = models.CharField(
+ max_length=32
+ )
+ phone_number_2 = models.CharField(
+ max_length=32
+ )
+```
+
+This single table could hold a couple
+of numbers,
+but this solution has some deficiencies.
+
+* What if an employee has more than two phone numbers?
+ It's possible for a person to have multiple cell phones,
+ a land line at their residence,
+ a pager number,
+ a fax number,
+ and so on.
+* How can we know what type of phone number is
+ in `phone_number_1` and `phone_number_2`?
+ If you pull the employee record
+ to try to call the individual
+ and dial a fax number instead,
+ you'd have a hard time talking to them.
+
+Instead,
+what if we had two separate models?
+
+```python
+# application/models.py
+from django.db import models
+
+class Employee(models.Model):
+ first_name = models.CharField(
+ max_length=100
+ )
+ last_name = models.CharField(
+ max_length=100
+ )
+ job_title = models.CharField(
+ max_length=200
+ )
+
+class PhoneNumber(models.Model):
+ number = models.CharField(
+ max_length=32
+ )
+ PHONE_TYPES = (
+ (1, 'Mobile'),
+ (2, 'Home'),
+ (3, 'Pager'),
+ (4, 'Fax'),
+ )
+ phone_type = models.IntegerField(
+ choices=PHONE_TYPES,
+ default=1
+ )
+```
+
+We've got two separate tables.
+How can we link the tables
+so that an employee can have one, two, or two hundred phone numbers?
+For that,
+we can use the `ForeignKey` relational field type.
+Here is a slightly updated version of `PhoneNumber`.
+
+```python
+...
+
+class PhoneNumber(models.Model):
+ number = models.CharField(
+ max_length=32
+ )
+ PHONE_TYPES = (
+ (1, 'Mobile'),
+ (2, 'Home'),
+ (3, 'Pager'),
+ (4, 'Fax'),
+ )
+ phone_type = models.IntegerField(
+ choices=PHONE_TYPES,
+ default=1
+ )
+ employee = models.ForeignKey(
+ Employee,
+ on_delete=models.CASCADE
+ )
+```
+
+This update says that every phone number must now be associated
+with an employee record.
+`on_delete` is a required attribute
+that determines what will happen
+when an employee record gets deleted.
+In this case, `CASCADE` means
+that deleting an employee row will cascade
+and delete any of the employee's related phone numbers too.
+
+The key to how this works lies in understanding *keys*.
+When you make a Django model,
+you get an extra field added
+to your model
+by the framework.
+This field is called an `AutoField`.
+
+```python
+# This is what Django adds to your model.
+id = models.AutoField(primary_key=True)
+```
+
+An `AutoField` adds a column
+to a database table
+that will assign each row
+in the table
+a unique integer.
+Each new row increments
+from the previous row
+and numbering starts
+at one.
+This number is the identifier
+for the row
+and is called the *primary key*.
+
+If Tom Bombadil is the first employee
+in the table,
+then the row `id` value would be `1`.
+
+Knowing about primary keys,
+we're equipped to understand foreign key fields.
+In the case
+of the `PhoneNumber.employee` foreign key field,
+any phone number row will store the value
+of some employee row's primary key.
+This is usually called a one to many relationship.
+
+A `ForeignKey` is a one to many relationship
+because multiple rows from a table
+(in this case, `PhoneNumber`)
+can reference a single row
+in another table, namely, `Employee`.
+In other words,
+an employee can have multiple phone numbers.
+If we wanted to get Tom's phone numbers,
+then one possible way would be:
+
+```python
+tom = Employee.objects.get(
+ first_name='Tom',
+ last_name='Bombadil'
+)
+phone_numbers = PhoneNumber.objects.filter(employee=tom)
+```
+
+The query for `phone_numbers` would be:
+
+```sql
+SELECT
+ "application_phonenumber"."id",
+ "application_phonenumber"."number",
+ "application_phonenumber"."phone_type",
+ "application_phonenumber"."employee_id"
+FROM "application_phonenumber"
+WHERE "application_phonenumber"."employee_id" = 1
+```
+
+In the database,
+Django will store the table column
+for the foreign key
+as `employee_id`.
+The query is asking for all phone number rows
+that match when the employee ID is 1.
+Since primary keys have to be unique,
+that value of 1 can only match Tom Bombadil
+so the resulting rows will be phone numbers
+that are associated
+with that employee.
+
+There is another relational field type
+that we should spend time on.
+That field is the `ManyToManyField`.
+As you might guess,
+this field is used when two types
+of data relate to each other
+in a many to many fashion.
+
+Let's think about neighborhoods.
+Neighborhoods can have a variety
+of mixed residence types
+like houses, apartments, condominiums
+and so on,
+but to keep the example simpler,
+we'll assume that neighborhoods are composed
+of houses.
+Each house in a neighborhood is the home
+of one or more people.
+
+What if we tried to model this with `ForeignKey` fields?
+
+```python
+# application/models.py
+from django.db import models
+
+class Person(models.Model):
+ name = models.CharField(
+ max_length=128
+ )
+
+class House(models.Model):
+ address = models.CharField(
+ max_length=256
+ )
+ resident = models.ForeignKey(
+ Person,
+ on_delete=models.CASCADE
+ )
+```
+
+This version shows a scenario
+where a house can only have a single resident.
+A person could be the resident
+of multiple houses,
+but those houses would be pretty lonely.
+What if we put the foreign key
+on the other side
+of the modeling relationship?
+
+```python
+# application/models.py
+from django.db import models
+
+class House(models.Model):
+ address = model.CharField(
+ max_length=256
+ )
+
+class Person(models.Model):
+ name = models.CharField(
+ max_length=128
+ )
+ house = models.ForeignKey(
+ House,
+ on_delete=models.CASCADE
+ )
+```
+
+In this version,
+a house can have multiple residents,
+but a person can only belong to a single house.
+
+Neither of these scenarios model the real world well.
+In the real world,
+houses can and do often hold multiple people.
+Simultaneously,
+many people in the world have a second house
+like a beach house
+or a summer cottage in the woods.
+Both sides of the model relationship can have many
+of the other side.
+
+With a `ManyToManyField`,
+you can add the field to either side.
+Here's the new modeling.
+
+```python
+# application/models.py
+from django.db import models
+
+class Person(models.Model):
+ name = models.CharField(
+ max_length=128
+ )
+
+class House(models.Model):
+ address = models.CharField(
+ max_length=256
+ )
+ residents = models.ManyToManyField(
+ Person
+ )
+```
+
+How does this work at the database level?
+We saw with foreign keys
+that one table can hold the primary key
+of another table's row
+in its own data.
+Unfortunately,
+a single database column cannot hold multiple foreign keys.
+That means that the modeling above does *not* add `residents`
+to the `House` table.
+Instead,
+the relationship is handled
+by adding a *new* database table.
+This new table contains the mapping
+between people and houses
+and stores rows
+that contain primary keys from each model.
+
+Let's think of an example
+to see what this looks like.
+Suppose there are three `Person` records
+with primary keys of 1, 2, and 3.
+Let's also suppose that there are three `House` records
+with primary keys of 97, 98, and 99.
+To prove that the many-to-many relationship works
+in both directions,
+we'll assume these conditions are true:
+
+* People with primary keys of 1 and 2 reside in house 97.
+* The person with primary key 3 owns house 98 and 99.
+
+The data in the new mapping table between `Person` and `House`
+would contain data like:
+
+```text
+Person | House
+-------|------
+1 | 97
+2 | 97
+3 | 98
+3 | 99
+```
+
+Because of the joining table,
+Django is able to query either side of the table
+to get related houses or residents.
+
+We can access the "many" side
+of each model using a queryset.
+`residents` will be a `ManyRelatedManager`
+and, like other managers,
+can provide querysets
+by using certain manager methods.
+
+Getting the reverse direction is a little less obvious.
+Django will add another `ManyRelatedManager`
+to the `Person` model automatically.
+The name of that manager is the model name joined with `_set`.
+In this case,
+that name is `house_set`.
+You can also provide a `related_name` attribute
+to the `ManyToManyField` if you want a different name
+like if you wanted to call it `houses` instead.
+
+```python
+house = House.objects.get(
+ address='123 Main St.'
+)
+# Note the use of `all()`!
+for resident in house.residents.all():
+ print(resident.name)
+
+person = Person.objects.get(name='Joe')
+for house in person.house_set.all():
+ print(house.address)
+```
+
+Understanding the Django models fields
+of `ForeignKey` and `ManyToManyField` is an important step
+to modeling your problem domain well.
+By having these tools available to you,
+you can begin to create many
+of the complex data relationships that exist
+with real world problems.
+
+## Summary
+
+{{< web >}}
+In this article,
+{{< /web >}}
+{{< book >}}
+In this chapter,
+{{< /book >}}
+we've explored:
+
+* How to set up a database for your project.
+* How Django uses special classes called models to keep data.
+* Running the commands that will prepare a database
+ for the models you want to use.
+* Saving new information into the database.
+* Asking the database for information that we stored.
+* Complex field types to model real world problems.
+
+With this ability to store data
+into a database,
+**you have all the core tools
+for building an interactive website
+for your users!**
+{{< web >}}
+In this series,
+{{< /web >}}
+{{< book >}}
+In this book,
+{{< /book >}}
+we have examined:
+
+* URL handling
+* views to run your code and business logic
+* templates to display your user interface
+* forms to let users input and interact with your site
+* models to store data into a database for long term storage
+
+This is the core set of features
+that most websites have.
+Since we've seen the core topics
+that make Django sites work,
+we're ready to focus our attention
+on some of the other amazing tools
+that set Django apart
+from the pack.
+
+First on the list is the built-in
+Django administrators site
+that lets you explore the data
+that you store
+in your database.
+We'll cover:
+
+* What the Django admin site is
+* How to make your models appear in the admin
+* How to create extra actions that your admin users can do
+
+{{< web >}}
+If you'd like to follow along
+with the series,
+please feel free to sign up
+for my newsletter
+where I announce all of my new content.
+If you have other questions,
+you can reach me online
+on Twitter
+where I am
+{{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
+{{< /web >}}
+
diff --git a/content/understand-django/2020-08-26-administer-all-the-things.pt.md b/content/understand-django/2020-08-26-administer-all-the-things.pt.md
new file mode 100644
index 00000000..de0b6ce8
--- /dev/null
+++ b/content/understand-django/2020-08-26-administer-all-the-things.pt.md
@@ -0,0 +1,1008 @@
+---
+title: "Administer All The Things"
+description: >-
+ This article will look
+ at how maintainers
+ of an application
+ can manage their data
+ through Django's built-in administrative tools.
+ We will see how to build admin pages
+ and customize the admin tools
+ to help teams navigate their apps.
+image: img/django.png
+type: post
+categories:
+ - Python
+ - Django
+tags:
+ - Python
+ - Django
+ - admin
+series: "Understand Django"
+
+---
+
+{{< web >}}
+In the previous
+[Understand Django]({{< ref "/understand-django/_index.md" >}})
+article,
+we used models
+to see how Django stores data
+in a relational database.
+{{< /web >}}
+We covered all the tools
+to bring your data to life
+in your application.
+{{< web >}}
+In this article,
+{{< /web >}}
+{{< book >}}
+In this chapter,
+{{< /book >}}
+we will focus
+on the built-in tools
+that Django provides
+to help us manage that data.
+
+{{< understand-django-series "admin" >}}
+
+## What Is The Django Admin?
+
+When you run an application,
+you'll find data
+that needs special attention.
+Maybe you're creating a blog
+and need to create and edit tags or categories.
+Perhaps you have an online shop
+and need to manage your inventory.
+Whatever you're building,
+you'll probably have to manage *something*.
+
+How can you manage that data?
+
+* If you're a programmer,
+ you can probably log into your server,
+ fire up a Django management shell,
+ and work with data directly
+ using Python.
+* If you're not a programmer,
+ well,
+ I guess you're out of luck!
+ **Nope, that's not true!**
+
+Django includes a web administrative interface
+that can help programmers and non-programmers alike.
+This administrative interface is usually called the Django admin,
+for short.
+
+Like so many other extensions
+in the Django ecosystem,
+the admin site is a Django application.
+The site is so commonly used
+that it is pre-configured
+when you run the `startproject` command.
+
+Before proceeding,
+I'd first like to make note of a security issue.
+When using `startproject`,
+Django will put the admin site
+at `/admin/`
+by default.
+**Change this**.
+The starter template conveniently sets up the admin site
+for you,
+but this default URL makes it easy
+for {{< extlink "https://en.wikipedia.org/wiki/Script_kiddie" "script kiddies" >}}
+to try to attack your admin site
+to gain access.
+Putting your admin site on a different URL *won't* fully protect your site
+(because you should never rely
+on "security through obscurity"),
+but it will help avoid a large amount
+of automated attacks.
+
+The Django admin gives you a quick ability
+to interact
+with your models.
+As you will see shortly,
+you can register a model
+with the admin site.
+Once the model is registered,
+you can use the site interface
+to perform CRUD operations
+on the data.
+
+CRUD is an acronym
+that describes the primary functions
+of many websites.
+The acronym stands for:
+
+* **Create** - A website can create data (i.e., insert data into the database)
+* **Read** - Users can see the data
+* **Update** - Data can be updated by users
+* **Delete** - A user can delete data from the system
+
+If you think about the actions
+that you take on a website,
+most actions will fall
+into one of those four categories.
+
+The admin site provides tools
+for doing all of those operations.
+There are a few main pages
+that you can navigate
+when working in a Django admin site
+that direct where the CRUD operations happen.
+These pages are available to you
+with very little effort
+on your part
+aside from the registration process
+that you'll see
+in the next section.
+
+1. Admin index page -
+ This page will show all the models,
+ grouped by the Django application they originate from,
+ that are registered with the admin.
+2. Model list page -
+ The list page shows rows of data
+ from a model (i.e., a database table).
+ From this page,
+ an administrator can perform actions
+ on multiple database records
+ like deleting a set of records
+ in a single operation.
+3. Add model page -
+ The admin provides a page
+ where new model instances can be created
+ using automatically generated forms
+ based on the model's fields.
+4. Model change page -
+ The change page lets you update an existing model instance
+ (i.e., a database table row).
+ From this page,
+ you can also delete a model instance.
+
+If you inspect this small set of pages,
+you'll notice
+that every part of the CRUD acronym can occur
+in this admin site.
+The power to create and destroy is in your hands. 😈
+
+Now that we understand what is in the admin site,
+let's focus on how to add your models to the admin.
+
+## Register A Model With The Admin
+
+To make the admin site show your model data,
+we need to update `admin.py`.
+On a new application created
+with `startapp`,
+you'll find
+that the `admin.py` file is largely empty.
+We need to provide a bit
+of glue
+so that the admin knows
+about a model.
+
+The admin site expects a `ModelAdmin` class
+for every model
+that you want to see displayed
+within the site.
+
+Let's consider a crude modeling
+of a book.
+
+```python
+# application/models.py
+from django.db import models
+
+class Book(models.Model):
+ title = models.CharField(
+ max_length=256
+ )
+ author = models.CharField(
+ max_length=256
+ )
+```
+
+Now we can create a `ModelAdmin` class
+for the `Book` model.
+
+```python
+# application/admin.py
+from django.contrib import admin
+
+from .models import Book
+
+@admin.register(Book)
+class BookAdmin(admin.ModelAdmin):
+ pass
+```
+
+There are a couple of important items
+to observe
+with this `admin.py` file.
+
+1. The `BookAdmin` is a subclass
+ of `admin.ModelAdmin`.
+2. The `BookAdmin` is registered
+ with the admin site
+ by using the `admin.register` decorator.
+
+You can also register an admin class
+by calling `register`
+after the class
+if you don't want to use a decorator.
+
+```python
+# application/admin.py
+from django.contrib import admin
+
+from .models import Book
+
+class BookAdmin(admin.ModelAdmin):
+ pass
+
+admin.site.register(Book, BookAdmin)
+```
+
+Now that we have a model registered
+with the admin site,
+how do we view it?
+Fire up your trusty development server
+with `runserver`
+and visit the URL
+that used to be `/admin/`
+(because you did change
+to something different from `/admin/`, right? Right!?).
+
+On this page,
+you'll encounter a login screen.
+We haven't worked through the authentication system yet,
+but, for now,
+we can understand that only user accounts
+that have a staff level permission
+can log in.
+
+Django provides a command
+that will let us create a user account
+with staff level permission
+and all other permissions.
+Like Linux operating systems,
+the user account
+with all permissions
+is called a superuser.
+You can create a superuser account
+with the `createsuperuser` command.
+
+```bash
+$ ./manage.py createsuperuser
+Username: matt
+Email address: matt@somewhere.com
+Password:
+Password (again):
+Superuser created successfully.
+```
+
+With a superuser account available,
+you're ready to log in
+to the admin site.
+Because you'll be using a superuser account,
+you will have permission
+to see every model
+that is registered
+with the admin site.
+
+Once you've logged in,
+you can view the `Book` model's admin page.
+Poke around!
+Create a book with the "Add Book" button.
+View the list page.
+Edit the book.
+Delete the book.
+You can see that with a tiny amount
+of work
+on your part,
+Django gives you a full CRUD interface
+for interacting with your model.
+
+We added the most simple `ModelAdmin` possible.
+The body of the class was a `pass`
+instead of any attributes.
+Django gives us a ton
+of options
+to let us control how our admin pages
+for `Book` will behave.
+Let's go on a tour
+of some commonly used admin attributes.
+
+## Customizing Your Admin
+
+Like many other parts of Django,
+the framework uses class level attributes
+to define the behavior
+of a class.
+Unlike forms and models
+where class level attributes are mostly fields
+that you're defining
+for yourself,
+`ModelAdmin` classes provide values
+for attributes
+that are well defined
+in the documentation.
+These attributes act as hooks
+that let you customize the behavior
+of your admin pages.
+
+Making effective admin pages is primarily about
+using these attributes
+so that the `ModelAdmin` class will do what you want.
+As such,
+mastering the Django admin site
+is all about mastering the `ModelAdmin` options
+that are listed
+{{< extlink "https://docs.djangoproject.com/en/4.1/ref/contrib/admin/#modeladmin-options" "in the documentation" >}}.
+That list is long,
+but don't be discouraged!
+I think that you can get about 80%
+of the value out of the Django admin
+by knowing only a handful
+of the options.
+
+When you poked around
+on the `Book` pages,
+you probably noticed
+that the listing of books is quite bland.
+The default list looks something
+like a list of links
+that show `Book object (#)`.
+We can change the look and utility
+of this page
+with a few different settings.
+
+Let's start with `list_display`.
+This `ModelAdmin` attribute controls
+which fields will appear
+on the list page.
+With our book model example,
+we could add the title to the page.
+
+```python
+# application/admin.py
+
+@admin.register(Book)
+class BookAdmin(admin.ModelAdmin):
+ list_display = ('id', 'title')
+```
+
+Django will make whatever is listed first
+into the link
+that a user can click
+to view the admin detail page
+for a model record.
+In this example,
+I'm using the `id` field
+as the link,
+but I could have used a single element tuple
+of `('title',)` to make the page show
+only the titles
+with the titles
+being the links.
+
+Sometimes you will have a type of model
+where you only want to see a subset
+of the records.
+Suppose that the `Book` model has a category field.
+
+```python
+# application/models.py
+
+class Book(models.Model):
+ class Category(
+ models.IntegerChoices
+ ):
+ SCI_FI = 1
+ FANTASY = 2
+ MYSTERY = 3
+ NON_FICTION = 4
+
+ # ... title and author from before
+
+ category = models.IntegerField(
+ choices=Category.choices,
+ default=Category.SCI_FI
+ )
+```
+
+By using the `list_filter` attribute,
+we can give the admin list page the ability
+to filter to the category
+that we want.
+
+```python
+# application/admin.py
+
+@admin.register(Book)
+class BookAdmin(admin.ModelAdmin):
+ list_display = ('id', 'title')
+ list_filter = ('category',)
+```
+
+The option will put a sidebar
+on the right side
+of the admin page.
+In that sidebar,
+you would see the categories
+that I included
+in the `Category` choices class.
+If I click on the "Fantasy" link,
+then my browser will navigate
+to `/admin/application/book/?category__exact=2`
+and will only display database rows
+that have a matching category.
+
+This isn't the only kind
+of filtering that the admin can do.
+We can also filter in time
+with the `date_hierarchy` field.
+Next,
+let's give the model a `published_date`.
+
+```python
+# application/models.py
+
+class Book(models.Model):
+ # ... title, author, category
+
+ published_date = models.DateField(
+ default=datetime.date.today
+ )
+```
+
+We can also change the `ModelAdmin`
+to use the new field.
+
+```python
+# application/admin.py
+
+@admin.register(Book)
+class BookAdmin(admin.ModelAdmin):
+ date_hierarchy = "published_date"
+ list_display = ("id", "title")
+ list_filter = ("category",)
+```
+
+By including the `date_hierarchy` attribute,
+the list page will contain some new user interface elements.
+Across the top of the page will be selectors
+to help filter down to the right time range.
+This is a very useful way to look through your database table.
+
+We can still go further.
+Perhaps we want all of the books to be sorted
+by their titles.
+Even if the `ordering` attribute is not set
+on the model's meta options,
+the `ModelAdmin` has its own `ordering` attribute.
+
+*What's "meta?"*
+Aside from fields,
+a Django model can set extra infromation
+about how to handle data.
+These extra options are the "meta" attributes
+of the model.
+A Django model adds meta info
+by including a nested `Meta` class
+on the model.
+Check out the
+{{< extlink "https://docs.djangoproject.com/en/4.1/ref/models/options/" "Model Meta options" >}}
+to see what other features are available
+to customize model behavior.
+
+```python
+# application/admin.py
+
+@admin.register(Book)
+class BookAdmin(admin.ModelAdmin):
+ date_hierarchy = "published_date"
+ list_display = ("id", "title")
+ list_filter = ("category",)
+ ordering = ("title",)
+```
+
+With this setting,
+all of the books
+on the page
+will be ordered by the title.
+The `ordering` attribute will add an appropriate
+`ORDER BY` clause to the database query
+via the admin-generated ORM `QuerySet`.
+
+The final convenient list page option
+that I want to highlight
+is the `search_fields` option.
+
+```python
+# application/admin.py
+
+@admin.register(Book)
+class BookAdmin(admin.ModelAdmin):
+ date_hierarchy = "published_date"
+ list_display = ("id", "title")
+ list_filter = ("category",)
+ ordering = ("title",)
+ search_fields = ("author",)
+```
+
+With this option,
+this list page will add a search bar
+to the top of the page.
+In the example,
+I added the ability to search based
+on the author
+of the book.
+
+When you search,
+your resulting URL could look
+like `/admin/application/book/?q=tolkien`.
+Django will do a case insensitive search
+on the field.
+The `QuerySet` would be something like:
+
+```python
+search_results = Book.objects.filter(
+ author__icontains="tolkien"
+)
+```
+
+The results wouldn't compete well
+compared to a dedicated search engine,
+but getting a decent search feature
+for a single line of code is awesome!
+
+The `ModelAdmin` also includes some useful settings
+to modify the behavior
+of the detail page
+of particular database records.
+
+For instance,
+let's assume
+that the `Book` model
+has a `ForeignKey`
+to track an editor.
+
+```python
+# application/models.py
+from django.contrib.auth.models import User
+
+class Book(models.Model):
+ # ... title, author, category
+ # published_date from before
+
+ editor = models.ForeignKey(
+ User,
+ null=True,
+ blank=True,
+ on_delete=models.CASCADE
+ )
+```
+
+On the admin page
+for an individual book,
+the `editor` field will be a dropdown
+by default.
+This field will include every `User` record
+in your application.
+If you have a popular site
+with thousands or millions of users,
+the page would be crushed
+under the weight
+of loading all those user records
+into that dropdown.
+
+Instead of having a useless page
+that you can't load,
+you can use `raw_id_fields`.
+
+```python
+# application/admin.py
+
+@admin.register(Book)
+class BookAdmin(admin.ModelAdmin):
+ date_hierarchy = "published_date"
+ list_display = ("id", "title")
+ list_filter = ("category",)
+ ordering = ("title",)
+ raw_id_fields = ("editor",)
+ search_fields = ("author",)
+```
+
+By using `raw_id_fields`,
+the admin changes from using a dropdown
+to using a basic text input
+which will display the foreign key
+of the user record.
+Seeing a foreign key number is visually less useful
+than seeing the actual name selected
+in a dropdown,
+but the `raw_id_fields` option adds two features
+to alleviate this.
+
+1. A search icon is present.
+ If users click on the icon,
+ a popup window appears
+ to let the user search
+ for a record
+ in a dedicated selection interface.
+2. If the record already has a foreign key
+ for the field,
+ then the string representation
+ of the record
+ will display next to the icon.
+
+Another option that can be useful
+is the `prepopulated_fields` option.
+Back in our discussion
+of URLs,
+we talked about slug fields.
+Slugs are often used
+to make pleasant URLs
+for detail pages
+showing an individual model instance.
+Let's add a `SlugField`
+to the `Book` model.
+
+```python
+# application/models.py
+
+class Book(models.Model):
+ # ... title, author, category
+ # published_date, editor from before
+
+ slug = models.SlugField()
+```
+
+What is the benefit
+of `prepopulated_fields`?
+By using this option,
+we can instruct the admin site
+to populate the `slug` field
+based on the `title`
+of the book.
+Here's the update to the `ModelAdmin`.
+
+```python
+# application/admin.py
+
+@admin.register(Book)
+class BookAdmin(admin.ModelAdmin):
+ date_hierarchy = "published_date"
+ list_display = ("id", "title")
+ list_filter = ("category",)
+ ordering = ("title",)
+ prepopulated_fields = {"slug": ("title",)}
+ raw_id_fields = ("editor",)
+ search_fields = ("author",)
+```
+
+Now when we want to add a new book
+in the admin,
+Django will use some JavaScript
+to update the slug field dynamically
+as we type the title!
+
+To this point,
+every attribute that we've added
+to the admin
+is static configuration.
+What do you do if you want to vary
+how the admin pages behave
+based on something dynamic?
+
+Thankfully,
+the Django team thought of that too.
+All of the options
+that we've examined
+have an equivalent method you can override
+that is prefixed
+with `get_`.
+For instance,
+if we want to control
+what fields users see
+on the list page
+based on who they are,
+we would implement `get_list_display`.
+In that method,
+we would return a tuple
+based on the user's access level.
+
+```python
+# application/admin.py
+from django.contrib import admin
+
+from .models import Book
+
+@admin.register(Book)
+class BookAdmin(admin.ModelAdmin):
+ ...
+
+ def get_list_display(self, request):
+ if request.user.is_superuser:
+ return (
+ 'id',
+ 'title',
+ 'author',
+ 'category',
+ )
+
+ return ('id', 'title')
+```
+
+One final attribute to consider
+is called `inlines`.
+I don't reach for this option often,
+but it's a convenient way
+to see *other* models
+that are related to a particular model.
+
+Suppose our sample application has reviews
+for books.
+We could add a model like:
+
+```python
+# application/models.py
+
+class Review(models.Model):
+ book = models.ForeignKey(
+ Book,
+ on_delete=models.CASCADE
+ )
+ rating = models.IntegerField()
+ comment = models.TextField()
+```
+
+To show other models
+on a detail page,
+we need to create an inline class
+and include it
+with the `ModelAdmin`.
+The result looks like:
+
+```python
+# application/admin.py
+from django.contrib import admin
+
+from .models import Book, Review
+
+class ReviewInline(admin.TabularInline):
+ model = Review
+
+@admin.register(Book)
+class BookAdmin(admin.ModelAdmin):
+ date_hierarchy = "published_date"
+ inlines = [ReviewInline]
+ list_display = ("id", "title")
+ list_filter = ("category",)
+ ordering = ("title",)
+ raw_id_fields = ("editor",)
+ prepopulated_fields = {"slug": ("title",)}
+ search_fields = ("author",)
+```
+
+By adding the inline class
+to the list of `inlines`,
+the detail page will show any reviews
+that are associated
+with a book.
+Additionally,
+you could create new reviews
+from the detail page
+since the admin will include a few blank forms
+by default.
+
+We've covered many options
+of the `ModelAdmin` class
+that you can use
+to customize your admin experience
+with common functions
+that many admin tools require.
+**What about the *uncommon* functions?**
+For extra customization,
+we can use admin actions.
+
+## Taking Action In The Admin
+
+When you want to do work
+related to specific records
+in your database,
+Django provides some techniques
+to customize your site
+and provide those capabilities.
+These customizations are called *actions*
+and they appear
+on the list page
+above the list
+of records.
+
+In the default admin site,
+there is an action
+that lets administrators delete records.
+If you select some rows
+with the checkboxes
+on the left hand side,
+select "Delete selected \",
+then click "Go",
+you will be presented
+with a page
+that asks for confirmation
+about deleting the rows you picked.
+
+The same kind of flow could be applied
+for any actions
+that you want
+to perform
+on database records.
+We can do this
+by adding a method
+on our `ModelAdmin`.
+
+The method must follow this interface:
+
+```python
+@admin.register(MyModel)
+class MyModelAdmin(admin.ModelAdmin):
+ actions = ['do_some_action']
+
+ def do_some_action(
+ self,
+ request: HttpRequest,
+ queryset: QuerySet
+ ) -> Optional[HttpResponse]:
+ # Do the work here.
+ ...
+```
+
+The queryset will represent the set
+of model records
+that the user selected.
+If the method returns `None`,
+then the user will be returned
+to the same admin page.
+If the method returns an `HttpResponse`,
+then the user will see that response
+(which is what happens
+with the delete confirmation page
+of the delete action).
+Whatever you do between the method being called
+and the method returning
+is up to you.
+
+Maybe our sample book application
+could set a book
+to premiere
+on the site
+as an important new available title.
+In this hypothetical scenario,
+we might have code
+that unsets any older premiere book
+or sends out emails
+to people who have expressed interest
+when new premieres are announced.
+
+For this scenario,
+we could add an action
+that would do these things.
+
+```python
+# application/admin.py
+
+def update_premiere(book):
+ """Pretend to update the book to be a premiere.
+
+ This function is to make the demo clear.
+ In a real application, this could be a manager method instead
+ which would update the book and trigger the email notifications
+ (e.g., `Book.objects.update_premiere(book)`).
+ """
+ print(f"Update {book.title} state to change premiere books.")
+ print("Call some background task to notify interested users via email.")
+
+@admin.register(Book)
+class BookAdmin(admin.ModelAdmin):
+ actions = ["set_premiere"]
+ date_hierarchy = "published_date"
+ inlines = [ReviewInline]
+ list_display = ("id", "title")
+ list_filter = ("category",)
+ ordering = ("title",)
+ raw_id_fields = ("editor",)
+ prepopulated_fields = {"slug": ("title",)}
+ search_fields = ("author",)
+
+ def set_premiere(
+ self,
+ request,
+ queryset
+ ):
+ if len(queryset) == 1:
+ book = queryset[0]
+ update_premiere(book)
+```
+
+Django will use the name
+of the method
+to set the label
+for the dropdown
+on the list page.
+In this case,
+the action label will be "Set premiere".
+
+We were able to extend the admin
+and hook into the page's user interface
+by defining a method
+and declaring it as an action.
+This is a powerful system
+to give administrators control
+and allow them
+to operate
+in custom ways
+on the data
+in their applications.
+
+## Summary
+
+{{< web >}}
+In this article,
+{{< /web >}}
+{{< book >}}
+In this chapter,
+{{< /book >}}
+we looked
+at the built-in Django administrator's site.
+This powerful extension gives us the ability
+to create, view, edit, and delete rows
+from database tables associated
+with your application's models.
+
+We've covered:
+
+* What the Django admin site is
+ and how to set it up
+* How to make your models appear in the admin
+* How to customize your admin pages quickly
+ with options
+ provided by the `ModelAdmin` class
+* How to create extra actions
+ that enable you to do work
+ on your model records
+
+{{< web >}}
+Next time we will cover
+{{< /web >}}
+{{< book >}}
+In the next chapter we will cover
+{{< /book >}}
+the anatomy
+of a Django application.
+A Django project is composed
+of many applications.
+We will explore:
+
+* The conventional structure of a Django app
+* How Django identifies and loads applications
+* Why applications are crucial for the Django ecosystem
+
+{{< web >}}
+If you'd like to follow along
+with the series,
+please feel free to sign up
+for my newsletter
+where I announce all of my new content.
+If you have other questions,
+you can reach me online
+on Twitter
+where I am
+{{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
+{{< /web >}}
+
diff --git a/content/understand-django/2020-09-29-anatomy-of-an-application.pt.md b/content/understand-django/2020-09-29-anatomy-of-an-application.pt.md
new file mode 100644
index 00000000..4598679c
--- /dev/null
+++ b/content/understand-django/2020-09-29-anatomy-of-an-application.pt.md
@@ -0,0 +1,638 @@
+---
+title: "Anatomy Of An Application"
+description: >-
+ This article explores applications.
+ Applications are core structural elements
+ of a Django project.
+ We will see the composition
+ of an app
+ and how to use them effectively.
+image: img/django.png
+type: post
+categories:
+ - Python
+ - Django
+tags:
+ - Python
+ - Django
+ - applications
+series: "Understand Django"
+
+---
+
+{{< web >}}
+In the previous
+[Understand Django]({{< ref "/understand-django/_index.md" >}})
+article,
+we got deep
+into the Django administrators site.
+We saw what the site was
+and how to configure and customize it.
+In this article,
+{{< /web >}}
+{{< book >}}
+In this chapter,
+{{< /book >}}
+we will examine what goes into an application.
+Applications are core elements
+of a Django project.
+
+{{< understand-django-series "apps" >}}
+
+## What Is An Application?
+
+Before getting to what a Django application **is**,
+we probably need to start with what it **is not**
+because the terminology is confusing.
+In the world of web development,
+developers may call a website a "web application."
+
+In Django parlance,
+a "web application" is a Django *project*.
+All of the pieces
+that come together to make a website
+are a project.
+The primary components
+within the project
+are called *applications*.
+In other words,
+a Django project is built
+from one or more Django applications.
+
+This situation is quite similar
+to Python packages.
+The software industry
+often describes the software unit
+as a "package."
+We think
+of `pip`,
+`npm`,
+or `apt`
+as "package" managers.
+This leads to a similar naming problem
+because Python also calls any directory
+with a `__init__.py` file a "package" as well.
+
+In reality,
+the code that you download
+using pip is technically called a "{{< extlink "https://packaging.python.org/overview/" "distribution" >}}."
+Even though we colloquially talk about Python downloads
+from PyPI
+(Python Package Index)
+as packages,
+we're really talking about distributions,
+and a distribution is a unit
+that contains one or more Python packages.
+
+Hopefully,
+you now understand
+the relationship
+of applications
+in Django.
+
+> Your "web application" is a Django **project**
+composed of one or more Django **applications**.
+
+## Application Structure
+
+Let's look at a fully loaded Django application
+to see the fairly standard structure
+that you will encounter
+in Django projects.
+
+An application usually tries
+to capture a core concept
+within your system.
+{{< web >}}
+For this article,
+{{< /web >}}
+{{< book >}}
+For this chapter,
+{{< /book >}}
+we will use movies
+as the concept we want to model.
+
+Let's see what a default scaffold includes,
+then build it up
+with all the extras.
+
+```bash
+(venv) $ ./manage.py startapp movies
+(venv) $ tree movies
+movies
+├── __init__.py
+├── admin.py
+├── apps.py
+├── migrations
+│ └── __init__.py
+├── models.py
+├── tests.py
+└── views.py
+```
+
+`admin.py`:
+This file is where all your `ModelAdmin` classes live
+to power how the movies app will appear
+in the Django admin.
+{{< web >}}
+You can learn more about the admin
+in [Administer All The Things]({{< ref "/understand-django/2020-08-26-administer-all-the-things.md" >}}).
+{{< /web >}}
+
+`apps.py`:
+This file is for the `AppConfig`
+of the application.
+We will discuss the `AppConfig`
+and how to use it
+{{< web >}}
+later in this article.
+{{< /web >}}
+{{< book >}}
+later in this chapter.
+{{< /book >}}
+
+`migrations`:
+This directory is where all database migrations are stored
+for the application.
+Any model changes for this app will generate a migration
+and create a numbered migration file
+in this directory.
+{{< web >}}
+More info about migrations is
+in [Store Data With Models]({{< ref "/understand-django/2020-06-25-store-data-with-models.md" >}}).
+{{< /web >}}
+
+`models.py`:
+This file is the home
+for all the Django `Model` classes
+in the application.
+The models represent all your database data.
+{{< web >}}
+Learn more about models
+in [Store Data With Models]({{< ref "/understand-django/2020-06-25-store-data-with-models.md" >}}).
+{{< /web >}}
+
+`tests.py`:
+This file is for automated tests.
+We'll cover automated tests
+in Django
+{{< web >}}
+in a future article.
+{{< /web >}}
+{{< book >}}
+in a future chapter.
+{{< /book >}}
+For now,
+you can know that I *always* **delete** this file
+and replace it with a `tests` package.
+A `tests` package is superior
+because you can split out to more focused files
+like `test_models.py`
+to know where the appropriate tests are.
+
+`views.py`:
+This file is where Django view functions or classes go.
+Views are the glue code
+that connect your URL routes
+to your database models.
+{{< web >}}
+I wrote about views
+in [Views On Views]({{< ref "/understand-django/2020-03-03-views-on-views.md" >}}).
+{{< /web >}}
+
+That's everything
+that comes
+with a generated app,
+but what other files are missing
+that you will commonly see
+in a Django application?
+
+`urls.py`:
+This file is often used
+to create routes
+that logically group
+all movie related functionality.
+The `urls.py` file would power all the routes
+in something
+like `www.mysite.com/movies/`.
+{{< web >}}
+Information
+on URLs is
+in [URLs Lead The Way]({{< ref "/understand-django/2020-01-22-urls-lead-way.md" >}}).
+{{< /web >}}
+
+`forms.py`:
+When you use Django `Form` classes
+to interact with users,
+this is the file
+where forms are stored.
+{{< web >}}
+You can discover more on forms
+in [User Interaction With Forms]({{< ref "/understand-django/2020-05-05-user-interaction-forms.md" >}}).
+{{< /web >}}
+
+`templatetags`:
+This directory is a Python package
+that would include a module
+like `movies_tags.py`
+where you'd define any custom template tags
+to use when rendering your templates.
+{{< web >}}
+Custom tags are a topic
+in [Templates For User Interfaces]({{< ref "/understand-django/2020-04-02-templates-user-interfaces.md" >}}).
+{{< /web >}}
+
+`templates`:
+This directory can store templates
+that the application will render.
+I personally prefer
+using a project-wide `templates` directory
+as discussed
+{{< web >}}
+in [Templates For User Interfaces]({{< ref "/understand-django/2020-04-02-templates-user-interfaces.md" >}}),
+{{< /web >}}
+{{< book >}}
+in the templates chapter,
+{{< /book >}}
+but `templates` directories are commonly found
+in indvidual Django apps,
+especially for third party applications
+that you may pull into your project.
+
+`static`:
+For static files
+that you want to display,
+such as images,
+you can use the `static` directory.
+We'll discuss static files more
+{{< web >}}
+in a future article.
+{{< /web >}}
+{{< book >}}
+in a future chapter.
+{{< /book >}}
+
+`management`:
+Users can extend Django
+with custom commands
+that can be called via `manage.py`.
+Those commands are stored
+in this package.
+Custom commands are a future topic
+{{< web >}}
+in this series.
+{{< /web >}}
+{{< book >}}
+in this book.
+{{< /book >}}
+
+`locale`:
+When doing translations and internationalization,
+the translation files must have a home.
+That's the purpose
+of the `locale` directory.
+
+`managers.py`:
+This file is not always used,
+but if your application has a lot
+of custom managers,
+then you may want
+to separate them
+from your models
+in this file.
+{{< web >}}
+Managers are a topic
+in [Store Data With Models]({{< ref "/understand-django/2020-06-25-store-data-with-models.md" >}}).
+{{< /web >}}
+
+Most applications will *not* have all
+of these pieces,
+but this should give you an idea
+of what things are when you are exploring
+Django apps in the wild
+on your own.
+Here's what our final sample tree
+would look like.
+
+```bash
+(venv) $ tree movies
+movies
+├── __init__.py
+├── admin.py
+├── apps.py
+├── forms.py
+├── locale
+│ └── es
+│ └── LC_MESSAGES
+│ ├── django.mo
+│ └── django.po
+├── management
+│ ├── __init__.py
+│ └── commands
+│ ├── __init__.py
+│ └── do_movie_stuff.py
+├── managers.py
+├── migrations
+│ ├── 0001_initial.py
+│ └── __init__.py
+├── models.py
+├── static
+│ └── movies
+│ └── moviereel.png
+├── templates
+│ └── movies
+│ ├── index.html
+│ └── movie_detail.html
+├── templatestags
+│ ├── __init__.py
+│ └── movies_tags.py
+├── tests
+│ ├── __init__.py
+│ ├── test_models.py
+│ └── test_views.py
+├── urls.py
+└── views.py
+```
+
+## Loading applications
+
+We've now seen
+what's in a Django application
+and have an idea
+of an app's composition.
+How does Django load applications?
+
+Django does *not* do automatic discovery
+of Django applications
+within your project.
+If you want Django
+to include an app
+in your project,
+you *must* add the app
+to your `INSTALLED_APPS` list
+in the settings file.
+
+This is a good example
+of Django following the Python ethos
+of favoring explicit
+over implicit.
+By being explicit,
+your project is not in danger
+of including apps
+that you don't expect.
+That might seem silly
+for apps that you write yourself,
+but you'll be thankful
+that's the case when some third party package
+in your virtual environment
+happens to have a Django app
+that you don't want
+in your project.
+
+On startup,
+when an application is
+in `INSTALLED_APPS`,
+Django will look
+for an `AppConfig` class.
+This class is stored
+in `apps.py`
+from the `startapp` command
+and contains metadata
+about the application.
+
+When Django starts,
+it will initialize the system
+by doing the following:
+
+* Load the settings
+* Configure logging (a topic we'll explore in the future)
+* Initialize an application registry
+* Import each package from the `INSTALLED_APPS`
+* Import a models module for each application
+* Invoke the `ready` method of every `AppConfig` discovered
+
+The `ready` method is a useful hook
+for taking action
+at startup.
+Since models are already loaded
+by the time the method is called,
+it's a safe place
+to interact with Django.
+
+If you attempt to run setup code
+before Django is ready,
+and you try to do something
+like use the ORM
+to interact with database data,
+you'll probably get an `AppRegistryNotReady` exception.
+Most apps won't directly need to run startup code,
+but knowing about the `ready` hook
+is a useful piece
+of knowledge
+to keep in your back pocket.
+
+## Ecosystem Applications
+
+An application is an important tool
+for grouping the different logical components
+in your project,
+but apps also serve another purpose.
+Apps are the basis
+for most of the 3rd party extensions
+in the Django ecosystem.
+
+A big reason to use Django is
+that the framework takes a "batteries included" approach.
+Most of the tools
+that you need to build a website are baked directly
+into the framework.
+This is a vastly different approach compared
+to {{< extlink "https://flask.palletsprojects.com/en/2.2.x/" "Flask" >}}
+which provides a relatively small API
+and depends heavily
+on third party libraries.
+
+Even though Django includes most
+of the major pieces
+for a web application,
+the framework doesn't include *everything*.
+When you want to include more features,
+Django apps fill in the gaps.
+
+Before you go out to PyPI,
+we need look no further
+than the `django.contrib` package,
+a collection of "contributed" applications provided
+by Django itself.
+When you run the `startproject` command,
+Django will include a variety
+of built-in applications
+that perform different functions.
+If you don't need some of the functionality,
+you can opt out
+by removing the app
+from your list
+in `INSTALLED_APPS`.
+
+I think this is the big difference
+in philosophy
+behind the framework.
+Some developers like to start
+from an extremely minimal kernel
+of functionality
+and build it up based on their needs.
+Django's philosophy seems to be
+that you start
+with an opinionated baseline
+and pare down what you don't require.
+Django doesn't expect that you'll use every feature
+in every app,
+but many of the features that you'll want
+are at the ready
+when you need them.
+
+From my point of view,
+I think the Django philosophy is the right one
+(shocking, isn't it!? 🤪).
+The benefit of the Django philosophy is
+that you leverage the knowledge
+of people who have built web apps
+for a very long time.
+Not only do you leverage that knowledge,
+you benefit from the polishing
+applied by the Django developers
+to integrate the different major systems
+into a consistent whole.
+What you're left with is a framework
+that feels like it belongs together,
+and I think that is a positive impact
+on your productivity.
+
+When you build from a minimal kernel
+and work up,
+you depend on knowing everything
+that's required to put something
+on the web.
+That means that you know all the pieces
+and how to bolt them together.
+But most people *don't* know all the pieces
+(because there are so many!).
+
+If you start minimally
+and don't know the pieces,
+you'll learn along the way,
+but what happens when you encounter a new concept
+that doesn't fit
+into your original mental model?
+For instance,
+security is a critical part
+that can destroy your mental model
+when you learn
+of a class of vulnerabilities
+that can restrict what is possible
+to do safely.
+When you follow this building from scratch approach,
+I think the final result will naturally be your own custom framework.
+If that's your thing, awesome.
+Go for it.
+For me,
+I want a framework
+that is a commodity
+and commonly understood
+by many people.
+
+Ok, so, what does this have to do with Django apps?
+Apps are contained and reusable modules.
+Because they have a fairly standard structure,
+a project can integrate a new app quickly.
+This means you can leverage the knowledge
+and experience
+(read: battle scars)
+of other web developers.
+The apps all play
+by the same rules
+so you,
+as the developer,
+spend less time gluing the app
+into your project
+and more time benefiting
+from what it does.
+
+I think this standard structure also makes it easier
+to experiment
+with new apps.
+When I need some new functionality,
+I will often check
+{{< extlink "https://djangopackages.org/" "Django Packages" >}}
+to look
+for apps
+that could meet my needs.
+In my experience,
+adding a new app is,
+in many cases,
+little more
+than installing the package,
+adding the app
+to the `INSTALLED_APPS` list,
+and putting an `include`
+in my `urls.py` file.
+Some packages require more configuration
+than that,
+but I think
+that the integration cost is low enough
+for me to experiment rapidly
+and back out my decision
+if I discover
+that an app won't do
+what I need.
+
+All in all,
+Django applications make working
+with the Django ecosystem
+a more enjoyable experience.
+
+## Summary
+
+{{< web >}}
+In this article,
+{{< /web >}}
+{{< book >}}
+In this chapter,
+{{< /book >}}
+we studied
+Django applications.
+
+We saw:
+
+* What a Django application is
+* How a Django application is structured
+* How the Django ecosystem benefits
+ from a common format
+ that creates reusable components
+
+{{< web >}}
+Next time we will look into authentication
+{{< /web >}}
+{{< book >}}
+Next, we will look into authentication
+{{< /book >}}
+in Django.
+We will study:
+
+* How users are created and managed
+* How to deal with permissions for users
+* How to work with users in your views and templates
+
+{{< web >}}
+If you'd like to follow along
+with the series,
+please feel free to sign up
+for my newsletter
+where I announce all of my new content.
+If you have other questions,
+you can reach me online
+on Twitter
+where I am
+{{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
+{{< /web >}}
+
diff --git a/content/understand-django/2020-11-04-user-authentication.pt.md b/content/understand-django/2020-11-04-user-authentication.pt.md
new file mode 100644
index 00000000..6177ca9a
--- /dev/null
+++ b/content/understand-django/2020-11-04-user-authentication.pt.md
@@ -0,0 +1,957 @@
+---
+title: "User Authentication"
+description: >-
+ Our focus in this Understand Django article
+ is how to manage users
+ in your Django application.
+ We'll study Django's built-in user authentication system.
+image: img/django.png
+type: post
+categories:
+ - Python
+ - Django
+tags:
+ - Python
+ - Django
+ - authentication
+ - authorization
+series: "Understand Django"
+
+---
+
+{{< web >}}
+In the previous
+[Understand Django]({{< ref "/understand-django/_index.md" >}})
+article,
+we learned about the structure
+of a Django *application*
+and how apps are the core components
+of a Django project.
+In this article,
+{{< /web >}}
+{{< book >}}
+In this chapter,
+{{< /book >}}
+we're going to dig into Django's built-in user authentication system.
+We'll see how Django makes your life easier
+by giving you tools
+to help your web application interact
+with the users
+of your site.
+
+{{< understand-django-series "auth" >}}
+
+## Authentication And Authorization
+
+We need to start with some terms
+before we begin our study.
+When your project interacts
+with users,
+there are two primary aspects tightly coupled
+to users
+that we must consider.
+
+*Authentication*:
+When a user tries to prove
+that they are who they say they are,
+that is authentication.
+A user will typically authenticate
+with your site
+via some login form
+or using a social provider
+like Google
+to verify their identity.
+
+> Authentication can only prove
+that {{< extlink "https://en.wikipedia.org/wiki/The_Important_Book" "you are you" >}}.
+
+*Authorization*:
+What is a user allowed to do?
+Authorization answers that question.
+We use authorization
+to determine what permissions or groups a user belongs to,
+so that we can scope what a user can do
+on the site.
+
+> Authorization determines what you can do.
+
+The Django auth system covers both of these topics.
+Sometimes the software industry will shorten authentication
+to "authn"
+and authorization to "authz,"
+but I think those labels are fairly silly
+and confusing.
+I will call out topics by their full name
+and refer to the entire Django system as "auth."
+
+## Setup
+
+If you used the `startproject` command
+to begin your project,
+then, congratulations,
+you're done
+and can move on!
+
+The auth features
+in Django require a couple
+of built-in Django applications
+and a couple
+of middleware classes.
+
+The Django apps are:
+
+* `django.contrib.auth` and
+* `django.contrib.contenttypes` (which the `auth` app depends on)
+
+The middleware classes are:
+
+* `SessionMiddleware` to store data about a user in a session
+* `AuthenticationMiddleware` to associate users with requests
+
+Middleware and sessions are both future topics
+so consider them internal details
+that you can ignore for now.
+
+The Django docs provide additional context
+about these prerequisites
+so check out the
+{{< extlink "https://docs.djangoproject.com/en/4.1/topics/auth/#installation" "auth topic installation section" >}}
+for more details.
+
+## Who Authenticates?
+
+If your site is going to have any level
+of personalization
+for anyone
+who uses it,
+then we need some way
+to track identity.
+
+In the Django auth system,
+identity is tracked
+with a `User` model.
+This model stores information
+that you'd likely want
+to associate
+with anyone who uses your site.
+The model includes:
+
+* name fields,
+* email address,
+* datetime fields
+ for when a user joins
+ or logs in to your site,
+* boolean fields
+ for some coarse permissions
+ that are very commonly needed,
+* and password data.
+
+The `User` model is a critically important model
+in many systems.
+Unless you're creating a website
+that is entirely public data
+and has no need
+to factor in identity,
+then you will probably use the `User` model heavily.
+
+Even if you *don't* expect your site's visitors
+to identify
+in some fashion,
+you'll still probably benefit
+from the `User` model
+because it is integrated
+with the Django admin site.
+I mentioned
+{{< web >}}
+in [Administer All The Things]({{< ref "/understand-django/2020-08-26-administer-all-the-things.md" >}})
+{{< /web >}}
+{{< book >}}
+in the Administer All The Things chapter
+{{< /book >}}
+that we needed a user
+with certain permissions
+to access the admin,
+but we glossed over the details
+of what that meant.
+
+The admin will only permit users
+with the `is_staff` attribute set
+to `True`.
+`is_staff` is one of the boolean fields
+that I listed
+as included
+in the default `User` model implementation.
+
+Now we have an understanding
+that the `User` model is a very important model
+in a Django site.
+At minimum,
+the model is important as you use the Django admin,
+but it can also be very important
+for the people
+that come to your site.
+
+Next,
+let's look a bit deeper
+at authentication
+and how that works
+in conjunction
+with the `User` model.
+
+## Authenticating With Passwords
+
+Like many other websites
+that you've used,
+Django's built-in auth system
+authenticates users
+with passwords.
+
+When a user wants to authenticate,
+the user must log in
+to the site.
+Django includes a `LoginView` class-based view
+that can handle the appropriate steps.
+The `LoginView` is a form view that:
+
+* Collects the `username` and `password`
+ from the user
+* Calls the `django.contrib.auth.authenticate` function
+ with the `username` and `password`
+ to confirm that the user is who they claim to be
+* Redirects to either a path that is set
+ as the value of the `next` parameter
+ in the URL's querystring
+ or `settings.LOGIN_REDIRECT_URL`
+ if the `next` parameter isn't set
+* Or, if authentication failed,
+ re-renders the form page
+ with appropriate error messages
+
+How does the `authenticate` function work?
+The `authenticate` function delegates the responsibility
+of deciding
+if the user's credentials are valid
+to an *authentication backend*.
+
+Like we have seen
+with templates
+and with databases,
+the auth system has swappable backends.
+With different backend options,
+you can have multiple ways
+of authenticating.
+The `authenticate` function will loop through any auth backends
+that are set
+in the `AUTHENTICATION_BACKENDS` list setting.
+Each backend can do one of three things:
+
+* Authenticate correctly with the user and return a `User` instance.
+* Not authenticate and return `None`.
+ In this case, the next backend is tried.
+* Not authenticate and raise a `PermissionDenied` exception.
+ In this case, no other backends are tried.
+
+You could add a backend
+to that setting
+that lets people authenticate
+with their social media accounts
+({{< extlink "https://django-allauth.readthedocs.io/en/latest/" "django-allauth" >}}
+is a great option to do exactly that).
+You might be in a corporate setting
+and need Single Sign-On (SSO)
+for your company.
+There are backend options
+that enable that too.
+
+Although there are many options,
+we'll focus
+on the built-in backend
+included with the auth system.
+The default backend is called the `ModelBackend`
+and it is in the `django.contrib.auth.backends` module.
+
+The `ModelBackend` is named as it is
+because it uses the `User` model
+to authenticate.
+Given a `username` and `password`
+from the user,
+the backend compares the provided data
+to any existing `User` records.
+
+The `authenticate` function calls the `authenticate` *method*
+that exists on the `ModelBackend`.
+The backend does a lookup
+of a `User` record
+based on the given `username` passed to the method
+by the `authenticate` function.
+If the user record exists,
+the backend calls `user.check_password(password)`
+where `password` is the actual password
+that is supplied
+by the person
+who submitted the POST
+to the `LoginView`.
+
+Django doesn't store actual passwords.
+To do so would be a major weakness
+in the framework
+because any leak
+of the database would leak all users' passwords.
+And that's totally not cool.
+Instead,
+the `password` field
+on the `User` model
+stores a *hash*
+of the password.
+
+Maybe you've never encountered hashing before.
+A hash is a computed value
+that is generated
+by running input data through a special function.
+The details of the computation is a very deep topic,
+especially when considering security,
+but the important thing
+to know about hashes is that you can't reverse the computation.
+
+In other words,
+if you generated a hash
+from `mysekretpassword`,
+then you wouldn't be able
+to take the hash value
+and figure out that the original input was `myseckretpassword`.
+
+Why is this useful?
+By computing hashes,
+Django can safely store
+that computed value
+without compromising a user's password.
+When a user wants to authenticate
+with a site,
+the user submits a password,
+Django computes the hash
+on that submitted value
+and *compares it to the hash stored
+in the database.*
+If the hashes match,
+then the site can conclude
+that the user sent the correct password.
+Only the password's hash
+would match the hash stored
+in the `User` model.
+
+Hashing is a fascinating subject.
+If you want to learn more about the guts
+of how Django manages hashes,
+I would suggest reading the
+{{< extlink "https://docs.djangoproject.com/en/4.1/topics/auth/passwords/" "Password management in Django" >}}
+docs
+to see the details.
+
+## Authentication Views
+
+That's a lot of stuff
+to do for authentication!
+
+Is Django going to expect you to call the `authenticate` function
+and wire together all the views yourself?
+No!
+
+I mentioned the `LoginView` earlier,
+but that's not the only view
+that Django provides
+to make authentication manageable.
+You can add the set of views
+with a single `include`:
+
+```python
+# project/urls.py
+
+from django.urls import include, path
+
+urlpatterns = [
+ ...
+ path(
+ "accounts/",
+ include("django.contrib.auth.urls")
+ ),
+]
+```
+
+This set includes a variety of features.
+
+* A login view
+* A logout view
+* Views to change a password
+* Views to reset a password
+
+If you choose to add this set,
+your job is to override the built-in templates
+to match the styling
+of your site.
+For example,
+to customize the logout view,
+you would create a file named `registration/logged_out.html`
+in your templates directory.
+The {{< extlink "https://docs.djangoproject.com/en/4.1/topics/auth/default/#all-authentication-views" "All authentication views" >}}
+documentation provides information
+about each view
+and the name of each template
+to override.
+Note that you *must* provide a template for the login view
+as the framework does not supply a default template for that view.
+
+If you have more complex needs
+for your site,
+you might want to consider some external Django applications
+that exist in the ecosystem.
+I personally like {{< extlink "https://django-allauth.readthedocs.io/en/latest/" "django-allauth" >}}.
+The project is very customizable
+and provides a path
+to add social authentication
+to sign up with your social media platform
+of choice.
+I also like django-allauth
+because it includes sign up flows
+that you don't have to build yourself.
+The application is definitely worth checking out.
+
+We've now seen how Django authenticates users
+to a website
+with the `User` model,
+the `authenticate` function,
+and the built-in authentication backend, `ModelBackend`.
+We've also seen how Django provides views
+to assist
+with login, logout, and password management.
+
+Once a user is authenticated,
+what is that user allowed to do?
+We'll see that next as we explore authorization
+in Django.
+
+## What's Allowed?
+
+### Authorization From User Attributes
+
+Django has multiple ways
+to let you control what a user is allowed
+to do
+on your site.
+
+The simplest form
+of checking
+on a user
+is to check
+if the site has identified the user or not.
+Before a user is authenticated
+by logging in,
+that user is anonymous.
+In fact,
+the Django auth system has a special class
+to represent this kind
+of anonymous user.
+Sticking to the principle
+of least surprise,
+the class is called `AnonymousUser`.
+
+The `User` model includes an `is_authenticated` attribute.
+Predictably,
+users that have authenticated will return `True`
+for `is_authenticated`
+while `AnonymousUser` instances return `False`
+for the same attribute.
+
+Django provides a `login_required` decorator
+that can use this `is_authenticated` information.
+The decorator will gate any view
+that needs a user to be authenticated.
+
+This may be the appropriate level
+of authorization check
+if you have an application
+that restricts who is allowed
+to log in.
+For instance,
+if you're running a Software as a Service (SaaS) application
+that requires users
+to pay a subscription
+to use the product,
+then you may have sufficient authorization checking
+by checking `is_authenticated`.
+In that scenario,
+if your application only permits users
+with an active subscription
+(or a trial subscription)
+to log in,
+`login_required` will guard against any non-paying users
+from using your product.
+
+There are other boolean values
+on the `User` model
+that you can use for authorization checking.
+
+* `is_staff` is a boolean to decide
+ whether a user is a staff member or not.
+ By default,
+ this boolean is `False`.
+ Only staff-level users are allowed
+ to use the built-in Django admin site.
+ You can also use the `staff_member_required` decorator
+ if you have views that should only be used
+ by members of your team
+ with that permission.
+* `is_superuser` is a special flag
+ to indicate a user
+ that should have access to everything.
+ This "superuser" concept is very similar
+ to the superuser that is present
+ in Linux permission systems.
+ There's no special decorator
+ for this boolean,
+ but you could use the `user_passes_test` decorator
+ if you had very private views
+ that you needed to protect.
+
+```python
+from django.contrib.admin.views.decorators import (
+ staff_member_required
+)
+from django.contrib.auth.decorators import (
+ user_passes_test
+)
+from django.http import HttpResponse
+
+@staff_member_required
+def a_staff_view(request):
+ return HttpResponse(
+ "You are a user with staff level permission."
+ )
+
+def check_superuser(user):
+ return user.is_superuser
+
+@user_passes_test(check_superuser)
+def special_view(request):
+ return HttpResponse(
+ "Super special response"
+ )
+```
+
+The `user_passes_test` decorator behaves much
+like `login_required`,
+but it accepts a callable
+that receives a user object
+and returns a boolean.
+If the boolean value is `True`,
+the request is permitted
+and the user gets the response.
+If the boolean value is `False`,
+the user will be redirected
+to the login page.
+
+### Authorization From Permissions And Groups
+
+The first set of checks
+that we looked at is data
+that is stored
+with a `User` model record.
+While that works well
+for some cases
+that apply to many sites,
+what about authorization
+that depends
+on what your application does?
+
+Django comes with a flexible permission system
+that can let your application control
+who can see what.
+The permission system includes some convenient auto-created permissions
+as well as the ability to make custom permission
+for whatever purpose.
+These permission records are `Permission` model instances
+from `django.contrib.auth.models`.
+
+Any time you create a new model,
+Django will create an additional set of permissions.
+These auto-created permissions map
+to the Create, Read, Update, and Delete (CRUD) operations
+that you can expect to use
+in the Django admin.
+For instance,
+if you have a `pizzas` app
+and create a `Topping` model,
+Django would create the following permissions:
+
+* `pizzas.add_topping` for Create
+* `pizzas.view_topping` for Read
+* `pizzas.change_topping` for Update
+* `pizzas.delete_topping` for Delete
+
+A big reason to create these permissions is to aid your development
+*and* add control to the Django admin.
+Staff-level users (i.e., `user.is_staff == True`)
+in your application have no permissions
+to start with.
+This is a safe default
+so that any new staff member cannot access all
+of the data
+in your system
+unless you grant them more permissions
+as you gain trust
+in them.
+
+When a staff user logs into the Django admin,
+they will initially see very little.
+As permissions are granted
+to the user's account,
+the Django admin will reveal additional information corresponding
+to the selected permissions.
+Although permissions are often granted
+through the `User` admin page,
+you can add permissions
+to a user
+through code.
+The `User` model has a `ManyToManyField`
+called `user_permissions`
+that associates user instances
+to particular permissions.
+
+Continuing with the pizza application example,
+perhaps you work with a chef
+for your pizza app.
+Your chef may need the ability
+to control any new toppings
+that should be available to customers,
+but you probably don't want the chef
+to be able to delete orders
+from the application's history.
+
+For the chef,
+you'd grant the `pizzas.add_topping`,
+`pizzas.view_topping`,
+and
+`pizzas.change_topping` permissions,
+but you'd leave out `orders.delete_order`.
+
+```python
+from django.contrib.auth.models import (
+ Permission, User
+)
+from django.contrib.contenttypes.models import (
+ ContentType
+)
+from pizzas.models import Topping
+
+content_type = ContentType.objects.get_for_model(
+ Topping
+)
+permission = Permission.objects.get(
+ content_type=content_type,
+ codename="add_topping"
+)
+chef_id = 42
+chef = User.objects.get(id=42)
+chef.user_permissions.add(permission)
+```
+
+We haven't covered the `contenttypes` app
+so this code may look unusual
+to you,
+but the auth system uses content types
+as a way
+to reference models generically
+when handling permissions.
+You can learn more about content types
+and their uses
+at {{< extlink "https://docs.djangoproject.com/en/4.1/ref/contrib/contenttypes/" "the contenttypes framework" >}}
+documentation.
+The important point
+to observe from the example
+is that permissions behave
+like any other Django model.
+
+Adding permissions to individual users is a nice feature
+for a small team,
+but if your team grows,
+it could devolve
+into a nightmare.
+
+Let's suppose that your application is wildly successful,
+and you need to hire a large support staff
+to help
+with customer issues.
+If your support team needs
+to view certain models
+in your system,
+it would be a total pain
+if you had to manage that
+per staff member.
+
+Django has an ability to create groups
+to alleviate this problem.
+The `Group` model is the intersection
+between a set of permissions
+and a set of users.
+Thus,
+you could create a group
+like "Support Team,"
+assign all the permissions
+that such a team should have,
+and include all your support staff
+on that team.
+Now,
+any time that the support staff members require a new permission,
+it can be added once
+to the group.
+
+A user's groups are tracked
+with another `ManyToManyField`
+called `groups`.
+
+```python
+from django.contrib.auth.models import (
+ Group, User
+)
+
+support_team = Group.objects.get(
+ name="Support Team"
+)
+support_sally = User.objects.get(
+ username="sally"
+)
+support_sally.groups.add(support_team)
+```
+
+In addition to the built-in permissions
+that Django creates
+and the group management system,
+you can create additional permissions
+for your own purposes.
+
+Let's give our chef permission
+to bake pizzas
+in our imaginary app.
+
+```python
+from django.contrib.auth.models import (
+ Permission, User
+)
+from django.contrib.contenttypes.models import (
+ ContentType
+)
+from pizzas.models import Pizza
+
+content_type = ContentType.objects.get_for_model(
+ Pizza
+)
+permission = Permission.objects.create(
+ codename="can_bake",
+ name="Can Bake Pizza",
+ content_type=content_type,
+)
+chef_id = 42
+chef = User.objects.get(id=42)
+chef.user_permissions.add(permission)
+```
+
+To check on the permission in our code,
+you can use the `has_perm` method
+on the `User` model.
+`has_perm` expects an application label
+and the permission codename
+joined together
+by a period.
+
+```python
+>>> chef = User.objects.get(id=42)
+>>> chef.has_perm('pizzas.can_bake')
+True
+```
+
+You can also use a decorator
+on a view
+to check a permission as well.
+The decorator will check the `request.user`
+for the proper permission.
+
+```python
+# pizzas/views.py
+
+from django.contrib.auth.decorators import permission_required
+
+@permission_required('pizzas.can_bake')
+def bake_pizza(request):
+ # Time to bake the pizza
+ # if you're allowed.
+ ...
+```
+
+## Working With Users In Views And Templates
+
+Now we've discussed how to authenticate users
+and how to check their authorization.
+How do we *interact* with users
+in your application code?
+
+The first way is inside of views.
+Part of configuring the auth system is to include the `AuthenticationMiddleware`
+in `django.contrib.auth.middleware`.
+
+This middleware has one job
+in request processing:
+add a `user` attribute to the `request`
+that the view will receive.
+This middleware gives us very clean
+and convenient access
+to the user record.
+
+```python
+# application/views.py
+
+from django.http import HttpResponse
+
+def my_view(request):
+ if request.user.is_authenticated:
+ return HttpResponse(
+ 'You are logged in.'
+ )
+ else:
+ return HttpResponse(
+ 'Hello guest!'
+ )
+```
+
+The `AuthenticationMiddleware` is what makes it possible
+for the decorators
+{{< web >}}
+that I've described in this article
+{{< /web >}}
+{{< book >}}
+that I've described in this chapter
+{{< /book >}}
+(i.e., `login_required`, `user_passes_test`, and `permission_required`)
+to work.
+Each of the decorators finds the `user` record
+as an attribute attached to the `request`.
+
+How about templates?
+If you had to add a user
+to a view's context
+for every view,
+that would be tedious.
+
+Thankfully,
+there is a context processor
+named `auth`
+that lets you avoid that pain
+(the processor is in `django.contrib.auth.context_processors`).
+The context processor will add a `user`
+to the context
+of every view
+when processing a request.
+
+Recall that a context processor is a function
+that receives a `request` object
+and returns a dictionary
+that will be merged
+into the context.
+Knowing that,
+can you guess how this context processor works?
+
+If you guessed `AuthenticationMiddleware`,
+you get a cookie! 🍪
+Since the middleware adds the `user`
+to the `request`,
+the context processor has the very trivial job
+of creating a dictionary
+like `{'user': request.user}`.
+There's a bit more to the actual implementation,
+and you can check out the
+{{< extlink "https://github.com/django/django/blob/4.1/django/contrib/auth/context_processors.py#L49" "Django source code" >}}
+if you want to see those details.
+
+What does this look like in practice?
+We've actually seen this already!
+One of the examples from the explanation
+of templates used the `user` context variable.
+Here's the example again so you don't need to jump back.
+
+{{< web >}}
+```django
+{% if user.is_authenticated %}
+ Welcome, {{ user.username }}
+{% endif %}
+```
+{{< /web >}}
+{{< book >}}
+```djangotemplate
+{% if user.is_authenticated %}
+ Welcome, {{ user.username }}
+{% endif %}
+```
+{{< /book >}}
+
+If you decide to use Django's permissions,
+you can also take advantage of the `perms` context variable
+in your templates.
+This variable is supplied
+by the `auth` context processor as well
+and gives your template access to the permissions
+of the `user` in a concise manner.
+The {{< extlink "https://docs.djangoproject.com/en/4.1/topics/auth/default/#permissions" "Django docs" >}} include some good examples
+of how the `perms` variable can be used.
+
+Now you've seen how Django leverages the auth middleware
+to make users easily accessible
+to your views and templates.
+
+## Summary
+
+{{< web >}}
+In this article,
+{{< /web >}}
+{{< book >}}
+In this chapter,
+{{< /book >}}
+we got into Django's built-in user auth system.
+
+We learned about:
+
+* How auth is set up
+* What the `User` model is
+* How authentication works
+* Django's built-in views for making a login system
+* What levels of authorization are available
+* How to access users in views and templates
+
+{{< web >}}
+Next time we'll study middleware
+{{< /web >}}
+{{< book >}}
+In the next chapter, we'll study middleware
+{{< /book >}}
+in Django.
+As the name implies,
+middleware is some code
+that exists
+in the "middle"
+of the request and response process.
+We will learn about:
+
+* The mental model for considering middleware
+* How to write your own middleware
+* Some of the middleware classes that come with Django
+
+{{< web >}}
+If you'd like to follow along
+with the series,
+please feel free to sign up
+for my newsletter
+where I announce all of my new content.
+If you have other questions,
+you can reach me online
+on Twitter
+where I am
+{{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
+{{< /web >}}
+
diff --git a/content/understand-django/2020-12-16-middleware-do-you-go.pt.md b/content/understand-django/2020-12-16-middleware-do-you-go.pt.md
new file mode 100644
index 00000000..4545684a
--- /dev/null
+++ b/content/understand-django/2020-12-16-middleware-do-you-go.pt.md
@@ -0,0 +1,781 @@
+---
+title: "Middleware Do You Go?"
+description: >-
+ The topic for this Understand Django article
+ is middleware.
+ We'll see what middleware is,
+ what it is used for
+ in a Django project,
+ and how to write your own.
+image: img/django.png
+type: post
+categories:
+ - Python
+ - Django
+tags:
+ - Python
+ - Django
+ - middleware
+series: "Understand Django"
+
+---
+
+{{< web >}}
+In the previous
+[Understand Django]({{< ref "/understand-django/_index.md" >}})
+article,
+we covered the built-in auth system.
+That article gave you a chance
+to see the `User` model,
+ways to login users
+with Django's authentication tools,
+and the features
+that make the authorization controls work.
+In that topic,
+middleware came up
+as an integral component.
+Now we're going to learn more
+about middleware
+and its function
+within a Django project.
+{{< /web >}}
+{{< book >}}
+In this chapter,
+we're going to learn more
+about middleware
+and its function
+within a Django project.
+{{< /book >}}
+
+{{< understand-django-series "middleware" >}}
+
+## How Should I Think About Middleware?
+
+To start this topic,
+let's figure out
+where middleware exists
+in a Django project.
+
+Middleware is code that exists
+in the middle.
+"In the middle of what?"
+you might ask.
+The "middle" is the code that executes between
+when an `HttpRequest`
+is created
+by the framework
+and when the code you wrote is called
+by Django.
+The "middle" can also refer
+to code that executes
+*after* your view completes
+but before Django translates the `HttpResponse`
+to bytes
+to send it over the network
+to a browser.
+
+Have you ever eaten an Everlasting Gobstopper?
+No,
+I don't mean the one
+from Willy Wonka
+that will last forever.
+An Everlasting Gobstopper is a hard, layered candy
+that changes colors and flavors
+as you keep it in your mouth
+until you finally get to a soft center.
+
+Middleware is kind of like those candy layers
+and your view code is like the soft center.
+My analogy breaks down when you think
+about how someone eats the candy.
+
+With the candy,
+you experience one layer at a time
+until you get to the middle
+and you're done.
+A more apt comparison to middleware would be
+to burrow *through* the layers
+and come out the other side,
+experiencing the same layers
+in the opposite order
+as the way you came in.
+
+What's shown below is a diagram
+of all the default middleware
+that is included
+when you run the `startproject` command.
+If you're a visual learner
+who didn't find my gobstopper analogy helpful,
+then I hope this picture will be more illustrative.
+
+{{< web >}}
+```text
+ +--------- SecurityMiddleware --------------+
+ |+-------- SessionMiddleware --------------+|
+ ||+------- CommonMiddleware --------------+||
+ |||+------ CsrfViewMiddleware -----------+|||
+ ||||+----- AuthenticationMiddleware ----+||||
+ |||||+---- MessageMiddleware ----------+|||||
+ ||||||+--- XFrameOptionsMiddleware ---+||||||
+ ||||||| |||||||
+HttpRequest =================> view function ==================> HttpResponse
+ ||||||| |||||||
+```
+{{< /web >}}
+{{< book >}}
+```text
+ Middleware layers
+ +------- Security ------------+
+ |+------ Session ------------+|
+ ||+----- Common ------------+||
+ |||+---- CsrfView ---------+|||
+ ||||+--- Authentication --+||||
+ |||||+-- Message --------+|||||
+ ||||||+- XFrameOptions -+||||||
+ ||||||| |||||||
+request ========> view function=======> response
+ ||||||| |||||||
+```
+{{< /book >}}
+
+How does Django make this layering work?
+When you start Django
+with an application server
+like Gunicorn,
+you have to give the application server the path
+to your WSGI module.
+We will cover application servers in a later topic,
+but, for now, know that an application server can run your Django app.
+If your project directory containing your settings file
+is called `project`,
+then calling Gunicorn looks like:
+
+```bash
+$ gunicorn project.wsgi
+```
+
+You'd have this setup if you ran `django-admin startproject project .`
+(including the last dot),
+but what's really needed by the application server is wherever your `wsgi.py`
+file is located in your project,
+*in module path form*.
+Adjust accordingly for your needs.
+
+Remember way back
+{{< web >}}
+in the first article
+of the series
+{{< /web >}}
+{{< book >}}
+in the first chapter
+{{< /book >}}
+that WSGI stands
+for the Web Server Gateway Interface
+and is the common layer
+that synchronous Python web apps must implement
+in order to work
+with Python application servers.
+Inside this `project.wsgi` module
+is a function called `get_wsgi_application`,
+imported from `django.core.wsgi`.
+
+`get_wsgi_application` does two things:
+
+{{< web >}}
+* Calls `django.setup` which does all the startup configuration
+ that we saw in the last article
+* Returns a `WSGIHandler` instance
+{{< /web >}}
+{{< book >}}
+* Calls `django.setup` which does all the startup configuration
+ that we saw in the last chapter
+* Returns a `WSGIHandler` instance
+{{< /book >}}
+
+As you might guess,
+the `WSGIHandler` is designed
+to make the WSGI interface work,
+but it is also a subclass
+of `django.core.handlers.base.BaseHandler`.
+This base handler class is where Django handles middleware setup.
+
+The base handler includes a `load_middleware` method.
+This method has the job
+of iterating through all the middleware listed
+in your `MIDDLEWARE` setting.
+As it iterates through the `MIDDLEWARE`,
+the method's primary objective is
+to include each middleware
+in the *middleware chain*.
+
+> The middleware chain is the Django gobstopper.
+
+The chain represents each instance of Django middleware,
+layered together,
+to produce the desired effect
+of allowing a request and response
+to pass through each middleware.
+
+Aside from building the middleware chain,
+`load_middleware` must do some other important configuration.
+
+{{< web >}}
+* The method handles synchronous and asynchronous middleware.
+ As Django increases its support
+ of async development,
+ the internals of Django need to manage the differences.
+ `load_middleware` makes some alterations
+ depending on what it can discover about a middleware class.
+* The method registers a middleware with certain *sets*
+ of middleware
+ based on the presence
+ of various hook methods.
+ We'll discuss those hooks later in this article.
+{{< /web >}}
+{{< book >}}
+* The method handles synchronous and asynchronous middleware.
+ As Django increases its support
+ of async development,
+ the internals of Django need to manage the differences.
+ `load_middleware` makes some alterations
+ depending on what it can discover about a middleware class.
+* The method registers a middleware with certain *sets*
+ of middleware
+ based on the presence
+ of various hook methods.
+ We'll discuss those hooks later in this chapter.
+{{< /book >}}
+
+That explains middleware's structure
+and how all the middleware interacts
+with the request and response lifecycle,
+but what does middleware do?
+
+We can use middleware
+for a wide variety
+of purposes.
+Because of the middleware chain,
+a successful HTTP request will pass
+through every middleware.
+This property of middleware makes it ideal
+for code that we want to execute globally
+for our Django project.
+
+For instance,
+{{< web >}}
+think back to our last article
+on
+[User Authentication]({{< ref "/understand-django/2020-11-04-user-authentication.md" >}}).
+In that article,
+{{< /web >}}
+{{< book >}}
+think back to our last chapter.
+In that chapter,
+{{< /book >}}
+we observed
+that Django's auth system is dependent
+on the `AuthenticationMiddleware`.
+This middleware has the singular job
+of adding a `user` property
+to every `HttpRequest` object
+that passes through the application
+before the request gets
+to view code.
+
+The `AuthenticationMiddleware` highlights some qualities
+that are good for middleware
+in Django.
+
+* A middleware should ideally have a narrow or singular objective.
+* A middleware should run a minimal amount of code.
+
+*Why?*
+Again,
+the answer is related
+to the middleware chain.
+Since the HTTP request will pass through every middleware
+in the chain,
+then we can see
+that *every middleware will execute
+for every request.*
+In other words,
+each middleware carries a performance overhead
+for each request.
+
+There **is** an exception to this behavior
+of the chain.
+A middleware early
+in the chain
+can prevent middleware later
+in the chain
+from running.
+
+For example,
+the `SecurityMiddleware` is first
+in the default middleware chain
+from a `startproject` generated project.
+This middleware is designed to do some checks
+to keep the application secure.
+One check is to look for a secure connection
+(i.e., a request using HTTPS)
+if HTTPS is configured.
+If a request comes to the application
+and uses HTTP
+instead of HTTPS,
+the middleware can return an `HttpResponsePermanentRedirect`
+that redirects to the same URL
+with `https://`
+and prevents the rest of the chain
+from running.
+
+Aside from this exceptional behavior
+in middleware,
+it's important
+to remember that,
+in most circumstances,
+each middleware will run
+for each request.
+We should be aware
+of that performance aspect
+when creating our own middleware.
+
+Now we're ready to learn
+about how we can create our own middleware!
+
+## How Can I Write My Own Custom Middleware?
+
+Let's assume that you've found a good case
+to create a middleware.
+You need something that happens
+with every request
+and that functionality has a narrow goal.
+
+You can begin
+with an empty middleware definition.
+In my example,
+we're going to put the middleware
+in a `middleware.py` file.
+
+```python
+# project/middleware.py
+class AwesomeMiddleware:
+ def __init__(self, get_response):
+ self.get_response = get_response
+
+ def __call__(self, request):
+ return self.get_response(
+ request
+ )
+```
+
+After creating the middleware,
+you add it to your settings.
+
+```python
+# project/settings.py
+
+MIDDLEWARE = [
+ 'django.middleware.security.SecurityMiddleware',
+ ...,
+ 'middleware.AwesomeMiddleware',
+]
+```
+
+*That's it!*
+This custom middleware doesn't do anything
+except slow performance slightly
+because it's an extra method call
+on every request.
+Since I put the middleware at the end
+of the `MIDDLEWARE` list,
+it will be the last middleware
+to run before a view receives a request
+and the first middleware
+with the chance
+to process a response.
+
+We can break down how this class works.
+
+* The `__init__` method gets a callable
+ that is conventionally named `get_response`.
+ The middleware is created during `load_middleware`
+ and the callable is a key part
+ of what makes the middleware chain work.
+ The callable will either call the next middleware
+ or the view
+ depending on where the current middleware is
+ in the chain.
+* The `__call__` method transforms the middleware instance itself
+ into a callable.
+ The method must call `get_response`
+ to ensure that the chain is unbroken.
+
+If you want to do extra work,
+you can make changes
+to the `__call__` method.
+You can modify `__call__`
+to process changes
+before or after the call
+of `get_response`.
+In the request/response lifecycle,
+changes before `get_response`
+occur before the view is called
+while changes after `get_response`
+can handle the `response` itself
+or any other post-request processing.
+
+Let's say we want our example middleware
+to record some timing information.
+We might update the code
+to look like:
+
+```python
+# project/middleware.py
+import logging
+import time
+
+logger = logging.getLogger(__name__)
+
+
+class AwesomeMiddleware:
+ def __init__(self, get_response):
+ self.get_response = get_response
+
+ def __call__(self, request):
+ before_timestamp = time.time()
+ logger.info(
+ f"Tracking {before_timestamp}"
+ )
+
+ response = self.get_response(request)
+
+ after_timestamp = time.time()
+ delta = after_timestamp - before_timestamp
+ logger.info(
+ f"Tracking {after_timestamp} for a delta of {delta}"
+ )
+
+ return response
+```
+
+We still haven't covered logging yet,
+but you can understand it
+as recording messages
+to some output source
+like a file.
+
+This example acts as a crude performance monitor.
+If you wanted to measure the response time
+of a view,
+this middleware would do that.
+The downside is that it wouldn't tell you *which* view is recorded.
+Hey, give me a break,
+this is a silly example! 🤪
+
+Hopefully,
+you're beginning
+to see how middleware can be useful.
+But wait!
+There's more that middleware can do.
+
+A Django middleware can define any
+of three different hook methods
+that Django will run
+at different parts of the request/response lifecycle.
+The three methods are:
+
+* `process_exception` - This hook is called
+ whenever a view raises an exception.
+ This could include an uncaught exception
+ from the view,
+ but the hook will also receive exceptions
+ that are intentionally raised
+ like `Http404`.
+* `process_template_response` - This hook is called
+ whenever a view returns a response
+ that looks like a template response
+ (i.e., the response object has a `render` method).
+* `process_view` - This hook is called
+ right before the view.
+
+Returning to our silly example,
+we can make it less silly
+by using the `process_view` hook.
+Let's see what we can do:
+
+```python
+# project/middleware.py
+import logging
+import time
+
+logger = logging.getLogger(__name__)
+
+
+class AwesomeMiddleware:
+ def __init__(self, get_response):
+ self.get_response = get_response
+
+ def __call__(self, request):
+ before_timestamp = time.time()
+ logger.info(
+ f"Tracking {before_timestamp}"
+ )
+
+ response = self.get_response(request)
+
+ after_timestamp = time.time()
+ delta = after_timestamp - before_timestamp
+ logger.info(
+ f"Tracking {after_timestamp} for a delta of {delta}"
+ )
+
+ return response
+
+ def process_view(self, request, view_func, view_args, view_kwargs):
+ logger.info(
+ f"Running {view_func.__name__} view"
+ )
+```
+
+Now our middleware uses Python's reflection capabilities
+to record the view's name.
+If accessing the Django admin
+with an unauthenticated user,
+the log might record something like:
+
+```text
+Tracking 1607438038.232886
+Running login view
+Tracking 1607438038.261855 for a delta of 0.02896881103515625
+```
+
+This middleware could still benefit
+from a lot of polish,
+but you can see how the hooks make it possible
+for a middleware to have more advanced functionality.
+
+As an example of the `process_exception` middleware,
+consider a service
+that collects and reports exceptions
+to track the health
+of your application.
+There are many of these services
+like
+{{< extlink "https://rollbar.com/" "Rollbar" >}}
+and
+{{< extlink "https://sentry.io/welcome/" "Sentry" >}}.
+I happen to be a Rollbar user
+so I'll comment
+on that one.
+You can see from the
+{{< extlink "https://github.com/rollbar/pyrollbar/blob/8d116a374f2c54da886972f7da7c289e317bbd8a/rollbar/contrib/django/middleware.py#L268" "pyrollbar code" >}}
+that the service sends exception information
+from the `process_exception` hook
+to Rollbar
+via their `rollbar.report_exc_info` function.
+Without middleware,
+capturing and reporting exceptions would be *significantly* harder.
+
+Want to learn more about hooks?
+You can see all the details
+about these hooks
+in the {{< extlink "https://docs.djangoproject.com/en/4.1/topics/http/middleware/#other-middleware-hooks" "middleware documentation" >}}.
+
+## What Middleware Does Django Include?
+
+We've looked at the mental model
+for middleware
+and all the details
+of how an individual middleware works.
+What middleware does Django include
+in the framework?
+
+The full list
+of built-in middleware
+is available
+in the {{< extlink "https://docs.djangoproject.com/en/4.1/ref/middleware/" "middleware reference" >}}.
+I'll describe what I think are the most common
+or useful middleware classes
+that Django includes.
+
+{{< web >}}
+* `AuthenticationMiddleware` - We've already encountered this middleware
+ in the exploration of the auth system.
+ The job of this middleware is
+ to add the `user` attribute
+ to an `HttpRequest` object.
+ That one little `user` attribute powers many
+ of the features of the auth system.
+* `CommonMiddleware` - The common middleware is a bit of an oddball.
+ This middleware handles a variety
+ of Django settings
+ to control certain aspects
+ of your project.
+ For instance,
+ the `APPEND_SLASH` setting will redirect a request
+ like `example.com/accounts`
+ to `example.com/accounts/`.
+ This setting only works
+ if the `CommonMiddleware` is included.
+* `CsrfViewMiddleware` - In the forms article,
+ I mentioned the CSRF token.
+ As a reminder,
+ this is a security feature
+ that helps protect your project against malicious sources
+ that want to send bad data
+ to your site.
+ The `CsrfViewMiddleware` ensures
+ that the CSRF token is present and valid
+ on form submissions.
+* `LocaleMiddleware` - This middleware is for handling translations
+ if you choose to internationalize your project.
+* `MessageMiddleware` - The message middleware is for "flash messages."
+ These are one-time messages
+ that you'd likely see
+ after submitting a form,
+ though they can be used
+ in many places.
+ We'll discuss messages more
+ when we get to the sessions topic.
+* `SecurityMiddleware` - The security middleware includes a number
+ of checks
+ to help keep your site secure.
+ We saw the example of checking for HTTPS earlier
+ in this article.
+ This middleware also handles things like XSS, HSTS,
+ and a bunch of other acronyms (😛)
+ that will be seen
+ with the future security topic.
+* `SessionMiddleware` - The session middleware manages
+ session state
+ for a user.
+ Sessions are crucial
+ for many parts of Django
+ like user auth.
+{{< /web >}}
+{{< book >}}
+* `AuthenticationMiddleware` - We've already encountered this middleware
+ in the exploration of the auth system.
+ The job of this middleware is
+ to add the `user` attribute
+ to an `HttpRequest` object.
+ That one little `user` attribute powers many
+ of the features of the auth system.
+* `CommonMiddleware` - The common middleware is a bit of an oddball.
+ This middleware handles a variety
+ of Django settings
+ to control certain aspects
+ of your project.
+ For instance,
+ the `APPEND_SLASH` setting will redirect a request
+ like `example.com/accounts`
+ to `example.com/accounts/`.
+ This setting only works
+ if the `CommonMiddleware` is included.
+* `CsrfViewMiddleware` - In the forms chapter,
+ I mentioned the CSRF token.
+ As a reminder,
+ this is a security feature
+ that helps protect your project against malicious sources
+ that want to send bad data
+ to your site.
+ The `CsrfViewMiddleware` ensures
+ that the CSRF token is present and valid
+ on form submissions.
+* `LocaleMiddleware` - This middleware is for handling translations
+ if you choose to internationalize your project.
+* `MessageMiddleware` - The message middleware is for "flash messages."
+ These are one-time messages
+ that you'd likely see
+ after submitting a form,
+ though they can be used
+ in many places.
+ We'll discuss messages more
+ when we get to the sessions topic.
+* `SecurityMiddleware` - The security middleware includes a number
+ of checks
+ to help keep your site secure.
+ We saw the example of checking for HTTPS earlier
+ in this chapter.
+ This middleware also handles things like XSS, HSTS,
+ and a bunch of other acronyms (😛)
+ that will be seen
+ with the future security topic.
+* `SessionMiddleware` - The session middleware manages
+ session state
+ for a user.
+ Sessions are crucial
+ for many parts of Django
+ like user auth.
+{{< /book >}}
+
+As you can see
+from this incomplete list,
+Django's middleware can do a lot
+to enrich your project
+in a wide variety
+of ways.
+Middleware is an extremely powerful concept
+for Django projects
+and a great tool
+to extend your application's request handling.
+
+Remember,
+middleware comes with a performance cost
+so avoid the temptation
+to stuff too much functionality
+into the middleware chain.
+As long as you're aware
+of the tradeoffs,
+middleware is a great tool
+for your toolbelt.
+
+## Summary
+
+{{< web >}}
+In this article,
+{{< /web >}}
+{{< book >}}
+In this chapter,
+{{< /book >}}
+we saw Django's middleware system.
+
+We discussed:
+
+* The mental model for considering middleware
+* How to write your own middleware
+* Some of the middleware classes that come with Django
+
+{{< web >}}
+Next time we'll dig into static files.
+{{< /web >}}
+{{< book >}}
+In the next chapter, we'll dig into static files.
+{{< /book >}}
+Static files are all the images,
+JavaScript,
+CSS,
+or other file types
+that your application serves, unmodified,
+to a user.
+We need to understand:
+
+* How to configure static files
+* The way to work with static files
+* How to handle static files
+ when deploying your site
+ to the internet
+
+{{< web >}}
+If you'd like to follow along
+with the series,
+please feel free to sign up
+for my newsletter
+where I announce all of my new content.
+If you have other questions,
+you can reach me online
+on Twitter
+where I am
+{{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
+{{< /web >}}
+
diff --git a/content/understand-django/2021-01-06-serving-static-files.pt.md b/content/understand-django/2021-01-06-serving-static-files.pt.md
new file mode 100644
index 00000000..550e1f14
--- /dev/null
+++ b/content/understand-django/2021-01-06-serving-static-files.pt.md
@@ -0,0 +1,800 @@
+---
+title: "Serving Static Files"
+description: >-
+ In this Understand Django article,
+ we'll examine static files.
+ Static files are critical to apps,
+ but have little to do with Python code.
+ We'll see what they are
+ and what they do.
+image: img/django.png
+type: post
+categories:
+ - Python
+ - Django
+tags:
+ - Python
+ - Django
+ - static
+series: "Understand Django"
+
+---
+
+In the previous
+{{< web >}}
+[Understand Django]({{< ref "/understand-django/_index.md" >}})
+article,
+{{< /web >}}
+{{< book >}}
+chapter,
+{{< /book >}}
+I described how Django gives us tools
+to run code
+for any request
+using the middleware system.
+Our next focus will be on static files.
+Static files are vital
+to your application,
+but they have little to do with Python code.
+We'll see what they are
+and what they do.
+
+{{< understand-django-series "static" >}}
+
+## What Are Static Files?
+
+Static files are files
+that don't change
+when your application is running.
+
+These files do a lot to improve your application,
+but they aren't dynamically generated
+by your Python web server
+like a usual HTML response.
+In a typical web application,
+your most common static files will be the following types:
+
+* Cascading Style Sheets, CSS
+* JavaScript
+* Images
+
+Keep in mind that even though Django will serve these files statically,
+there may be a complex process
+in place
+to produce the files.
+For instance,
+modern JavaScript apps often
+use complex build tools
+like {{< extlink "https://webpack.js.org/" "webpack" >}}
+to build the final JavaScript files
+that are served
+to users.
+
+Static files are crucial
+to your Django project
+because the modern web requires more than dynamically generated HTML markup.
+Do you visit any website
+that has *zero* styling
+of its HTML?
+These kinds of sites exist
+and can be awesome
+for making a quick tool,
+but most users expect websites
+to be aesthetically pleasing.
+For us,
+that means that we should be prepared
+to include some CSS styling
+at a minimum.
+
+Let's look at some configuration
+to see where static files live in your project,
+then begin to work with some examples.
+
+## Configuration
+
+To use static files
+in your project,
+you need the `django.contrib.staticfiles` app
+in your project's `INSTALLED_APPS` list.
+This is another one
+of the default Django applications
+that Django will include
+if you start from the `startproject` command.
+
+The `staticfiles` app has a handful
+of {{< extlink "https://docs.djangoproject.com/en/4.1/ref/settings/#settings-staticfiles" "settings" >}}
+that we need to consider to start.
+
+I'm going to make the same recommendation
+about static files
+as I did with templates.
+I recommend that you create a `static` directory
+at the root
+of your project
+to hold your static files.
+Similarly to templates,
+the `staticfiles` app will look for `static` directories
+within each of your Django apps
+to find files,
+but I find it easier to work with and locate static files
+if they are all
+in the same directory.
+
+To make that setup work,
+use the `STATICFILES_DIRS` setting.
+This setting tells Django any additional locations
+for static files beyond looking for a `static` directory
+within each app.
+
+```python
+# project/settings.py
+
+...
+
+STATICFILES_DIRS = [BASE_DIR / "static"]
+```
+
+Next,
+we can define the URL path prefix
+that Django will use
+when it serves a static file.
+Let's say you have `site.css`
+in the root
+of your project's `static` directory.
+You probably wouldn't want the file
+to be accessible as `mysite.com/site.css`.
+To do so would mean that static files could conflict
+with URL paths
+that your app might need to direct
+to a view.
+The `STATIC_URL` setting lets us namespace our static files
+and, as the {{< extlink "https://www.python.org/dev/peps/pep-0020/" "Zen of Python" >}} says:
+
+> Namespaces are one honking great idea -- let's do more of those!
+
+```python
+# project/settings.py
+
+...
+
+STATICFILES_DIRS = [BASE_DIR / "static"]
+STATIC_URL = '/static/'
+```
+
+With `STATIC_URL` set,
+we can access `site.css`
+from `mysite.com/static/site.css`.
+
+There's one more crucial setting
+that we need to set,
+and it is called `STATIC_ROOT`.
+When we deploy our Django project,
+Django wants to find all static files
+from a single directory.
+The reason for this is for efficiency.
+It's possible for Django
+to search through all the app `static` directories
+and any directories set
+in `STATICFILES_DIRS`
+whenever it searches for a file
+to serve,
+but that would be slow.
+
+Instead,
+Django will put all static files
+into a single directory
+so that searching for a file is a search
+through a single file tree.
+We'll look more at how this happens
+in the deployment section later
+{{< web >}}
+in this article.
+{{< /web >}}
+{{< book >}}
+in this chapter.
+{{< /book >}}
+
+Once we set `STATIC_ROOT`,
+Django will have the desired output location
+for static files.
+If you set the path somewhere
+in your repository,
+don't forget to put that path
+in your `.gitignore`
+if you're using version control
+with {{< extlink "https://git-scm.com/" "Git" >}}
+(and I highly recommend that you do!).
+Without that addition to `.gitignore`,
+you'll needlessly add the generated files
+to version control.
+I happen to set my `STATIC_ROOT`
+to a `staticfiles` directory.
+
+```python
+# project/settings.py
+
+...
+
+STATICFILES_DIRS = [BASE_DIR / "static"]
+STATIC_ROOT = BASE_DIR / "staticfiles"
+STATIC_URL = '/static/'
+```
+
+Now that we know how to configure static files,
+we're ready to see how to use them
+in our Django code.
+
+## Working With Static Files
+
+The primary way
+of working with static files
+is with a template tag.
+The `static` template tag will help render the proper URL
+for a static file for your site.
+
+Here's an example template to consider:
+
+{{< web >}}
+```django
+{% load static %}
+
+
+
+
+
+
+ Example of static template tag!
+
+
+```
+{{< /web >}}
+{{< book >}}
+```djangotemplate
+{% load static %}
+
+
+
+
+
+
+ Example of static template tag!
+
+
+```
+{{< /book >}}
+
+In this example,
+I'm assuming that there is a `css` directory
+in my `static` directory
+with a `site.css` file inside.
+Django will render this template tag
+as `/static/css/site.css`
+in the most basic format.
+We should also note
+that I had to include `{% load static %}`
+to ensure that the `static` template tag was available.
+
+In practice,
+I find that this `load` requirement bites me all the time.
+Thankfully, the `TemplateSyntaxError`
+that Django will raise provides a good clue
+on how to fix this issue.
+The exception says "Did you forget to register or load this tag?"
+How helpful of the Django developers
+to tell us what we're probably missing!
+
+Since we know that `STATIC_URL` is `/static/`
+from the configuration section,
+why don't I hardcode the link tag path
+to `/static/css/site.css`?
+You could,
+and that might work,
+but you'll probably run into some long term problems.
+
+* What if you ever wanted to change `STATIC_URL`?
+ Maybe you want to change it to something shorter like `/s/`.
+ If you hardcode the name,
+ now you have more than one place to change.
+* Using some extra features,
+ Django may change the name
+ of a file to something unique
+ by adding a hash to the file name.
+ With a hardcoded path of `/static/css/site.css`,
+ this may lead to a 404 response
+ if Django expects the unique name instead.
+ We'll see what the unique name is for
+ in the next section.
+
+We should remember
+to use the `static` tag
+in the same way
+that we use the `url` tag
+when we want to resolve a Django URL path.
+Both of these tags help avoid hardcoding paths
+that can change.
+
+Less commonly,
+we can refer to a static file
+from Python code.
+You can do this
+by calling a `static` function defined
+in the same location as the `static` template tag function,
+but the function is not located
+where you might expect it.
+Instead of importing
+from the `staticfiles` app,
+Django defines these functions
+in `django.templatetags.static`.
+
+For example,
+if you wanted to serve a JSON view
+that feeds a JavaScript client application the path
+to a CSS file,
+you might write:
+
+```python
+# application/views.py
+
+from django.http import JsonResponse
+from django.templatetags.static import (
+ static
+)
+
+def get_css(request):
+ return JsonResponse(
+ {'css': static('css/site.css')}
+ )
+```
+
+In my years of experience
+as a Django developer,
+I've only seen `static` used
+in views a handful
+of times.
+`static` is certainly more widely used
+in templates.
+
+When using static files,
+there are some important considerations
+for deploying your application
+for wider use
+on the internet.
+On its own,
+deployment is a large topic
+{{< web >}}
+that we'll cover in a future article,
+{{< /web >}}
+{{< book >}}
+that we'll cover in a future chapter,
+{{< /book >}}
+but we'll focus
+on static files deployment issues next.
+
+## Deployment Considerations
+
+In the configuration section,
+we saw the `STATIC_ROOT` option.
+That option will collect all the static files
+into a single directory,
+but *when* does it do that?
+And how do static files work
+when we run in development mode
+and don't have all the files
+in the `STATIC_ROOT` location?
+
+When you deploy your application
+to a server,
+one crucial setting
+to disable
+is the `DEBUG` setting.
+If `DEBUG` is on,
+all kinds of secret data can leak
+from your application,
+so the Django developers *expect* `DEBUG` to be `False`
+for your live site.
+Because of this expectation,
+certain parts of Django behave differently
+when `DEBUG` changes,
+and the `staticfiles` app is one such part.
+
+When `DEBUG` is `True`
+and you are using the `runserver` command
+to run the development web server,
+Django will search for files
+using a set of "finders"
+whenever a user requests a static file.
+These finders are defined
+by the `STATICFILES_FINDERS` setting,
+which defaults to:
+
+```python
+[
+ 'django.contrib.staticfiles.finders.FileSystemFinder',
+ 'django.contrib.staticfiles.finders.AppDirectoriesFinder',
+]
+```
+
+As you might guess,
+the `FileSystemFinder` looks for any static files
+found in the file system directory
+that we listed in `STATICFILES_DIRS`.
+The `AppDirectoriesFinder` looks for static files
+in the `static` directory
+of each Django application
+that you have.
+You can see how this gets slow
+when you realize
+that Django will walk
+through `len(STATICFILES_DIRS) + len(INSTALLED_APPS)`
+before giving up
+to find a single file.
+
+To make this whole process faster,
+we turn `DEBUG` to `False`.
+When `DEBUG` is `False`,
+all of the slow machinery
+that searches
+for files throughout your project
+for static file requests is turned off.
+Django only looks
+in the `STATIC_ROOT` directory
+for files.
+
+Since the finders are off
+when `DEBUG` is `True`,
+we have to make sure
+that `STATIC_ROOT` is filled
+with all the proper files.
+To put all the static files
+into place,
+you can use the `collectstatic` command.
+
+`collectstatic` will copy all the files it discovers
+from iterating through each finder
+and collecting files
+from what a finder lists.
+In my example below,
+my Django project directory is `myproject`,
+and I set `STATIC_ROOT` to `staticfiles`.
+
+```bash
+$ ./manage.py collectstatic
+
+42 static files copied to '/Users/matt/myproject/staticfiles'.
+```
+
+When deploying your application
+to your server,
+you would run `collectstatic`
+before starting the web server.
+By doing that,
+you ensure that the web server
+can access any static files
+that the Django app might request.
+
+Can we make this better?
+You bet!
+
+### Optimizing Performance In Django
+
+`staticfiles` has another setting worth considering.
+I didn't mention it in the configuration section
+because it's not a critical setting
+to make static files work,
+but we're ready for the setting
+as we're thinking about optimization.
+We should really consider this setting for our projects
+because Django is fairly slow at serving static files
+compared to some of the alternative options that are available
+in the ecosystem.
+
+The last setting we'll consider is the `STATICFILES_STORAGE` setting.
+This setting controls how static files are stored and accessed
+by Django.
+We may want to change `STATICFILES_STORAGE`
+to improve the efficiency
+of the application.
+The biggest boost we can get from this setting
+will provide file caching.
+
+In an ideal world,
+your application would only have to serve a static file exactly *one* time
+to a user's browser.
+In that scenario,
+if an application needed to use the file again,
+then the browser would reuse the *cached* file
+that it already retrieved.
+The challenge that we have is that static files (ironically?)
+change over time.
+
+Say,
+for instance,
+you changed `site.css`
+to change the styling
+of the application.
+You wouldn't want a browser
+to reuse the old version
+because it's missing the latest and greatest changes
+that you made.
+How do we get the benefit
+of telling a browser
+to cache a file for a long time
+to be as efficient as possible
+while still having the flexibility
+to make changes
+and make the user's browser fetch a new version
+of the file?
+
+The "trick" is to serve a "fingerprinted" version
+of the file.
+As a part of the deployment process,
+we would like to uniquely identify each file
+with some kind of version information.
+An easy way for a computer to do this is
+to take the file's content
+and calculate a hash value.
+We can have code take `site.css`,
+calculate the hash,
+and generate a file with the same content,
+but with a different filename
+like `site.abcd1234.css`
+if `abcd1234` was the generated hash value.
+
+The next part of the process is
+to make the template rendering use the `site.abcd1234.css` name.
+Remember how we used the `static` template tag
+instead of hardcoding `/static/css/site.css`?
+This example is a great reason why we did that.
+By using the `static` tag,
+Django can render the filename
+that includes the hash
+instead of only using `site.css`.
+
+The final bit that brings this scheme together is
+to tell the browser
+to cache `site.abcd1234.css`
+for a very long time
+by sending back a certain caching header
+in the HTTP response.
+
+Now,
+we've got the best of both worlds.
+
+* If the user fetches `site.abcd1234.css`,
+ their browser will keep it
+ for a long time
+ and never need to download it again.
+ This can be reused every time the user visits a page
+ in your app.
+* If we ever change `site.css`,
+ then the deployment process can generate a new file
+ like `site.ef567890.css`.
+ When the user makes a request,
+ the HTML will include the new version,
+ their browser won't have it in the cache,
+ and the browser will download the new version
+ with your new changes.
+
+Great!
+How do we get this
+and how much work is it going to require?
+The answer comes back
+to the `STATICFILES_STORAGE` setting
+and a tool called
+{{< extlink "http://whitenoise.evans.io/en/stable/" "WhiteNoise" >}}
+(get it!? "white noise" *is* "static." har har).
+
+WhiteNoise is a pretty awesome piece of software.
+The library will handle
+that *entire* caching scheme
+that I described above.
+
+To set up WhiteNoise,
+you install it with `pip install whitenoise`.
+Then,
+you need to change your `MIDDLEWARE` list
+and `STATICFILES_STORAGE` settings.
+
+```python
+# project/settings.py
+
+...
+
+MIDDLEWARE = [
+ 'django.middleware.security.SecurityMiddleware',
+ 'whitenoise.middleware.WhiteNoiseMiddleware',
+ # ...
+]
+
+STATICFILES_STORAGE = \
+ 'whitenoise.storage.CompressedManifestStaticFilesStorage'
+```
+
+That's about it!
+With this setup,
+WhiteNoise will do a bunch
+of work
+during the `collectstatic` command.
+The library will generate fingerprinted files
+like `site.abcd1234.css`,
+and it will generate compressed versions
+of those files
+using the gzip compression algorithm
+(and, optionally, the
+{{< extlink "https://en.wikipedia.org/wiki/Brotli" "brotli" >}}
+compression algorithm).
+Those extra files look
+like `site.abcd1234.css.gz`
+or `site.abcd1234.css.br`.
+
+When your application runs,
+the WhiteNoise middleware will handle which files
+to serve.
+Because files are static
+and don't require dynamic processing,
+we include the middleware high
+on the list
+to skip a lot of needless extra Python processing.
+In my configuration example,
+I left the `SecurityMiddleware` above WhiteNoise
+so the app can still benefit
+from certain security protections.
+
+As a user's browser makes a request
+for a fingerprinted file,
+the browser can include a request header
+to indicate what compressed formats it can handle.
+Sending compressed files is *way* faster
+than sending uncompressed files over a network.
+WhiteNoise will read the appropriate header
+and try to respond
+with the gzip or brotli version.
+
+The scheme that I described is not the only way
+to handle static files.
+In fact,
+there are some tradeoffs
+to think about:
+
+1. Building with WhiteNoise means
+ that we only need to deploy a single app
+ and let Python handle all
+ of the processing.
+2. Python, for all its benefits,
+ is not the fastest programming language out there.
+ Leaving Python to serve your static requests will run slower
+ than some other methods.
+ Additionally,
+ your web server's processes must spend time serving the static files
+ rather than being fully devoted to dynamic requests.
+
+### Optimizing Performance With A Reverse Proxy
+
+An alternative approach
+to using Django
+to serve static files
+is to use another program
+as a *reverse proxy*.
+This setup is more complex,
+but it can offer better performance
+if you need it.
+A reverse proxy is software
+that sits between your users
+and your Django application server.
+CloudFlare has a
+{{< extlink "https://www.cloudflare.com/learning/cdn/glossary/reverse-proxy/" "good article" >}}
+if you want to understand why "reverse" is in the name.
+
+If you set up a reverse proxy,
+you can instruct it
+to handle many things,
+including URL paths
+coming to your site's domain.
+This is where `STATIC_ROOT` and `collectstatic` are useful outside of Django.
+You can set a reverse proxy
+to serve all the files
+that Django collects
+into `STATIC_ROOT`.
+
+The process is roughly:
+
+1. Run `collectstatic`
+ to put files into `STATIC_ROOT`.
+2. Configure the reverse proxy
+ to handle any URL pattern
+ that starts with `STATIC_URL`
+ (recall `/static/` as an example)
+ and pass those requests
+ to the directory structure
+ of `STATIC_ROOT`.
+3. Anything that doesn't look
+ like a static file (e.g., `/accounts/login/`)
+ is delegated
+ to the app server running Django.
+
+In this setup,
+the Django app never has to worry
+about serving static files
+because the reverse proxy
+takes care of those requests
+before reaching the app server.
+The performance boost comes
+from the reverse proxy itself.
+Most reverse proxies are designed
+in very high performance languages
+like C
+because they are designed
+to handle a specific problem:
+routing requests.
+This flow lets Django handle the dynamic requests
+that it needs to
+and prevents the slower Python processes
+from doing work
+that reverse proxies are built for.
+
+If this kind of setup appeals
+to you,
+one such reverse proxy
+that you can consider is
+{{< extlink "https://www.nginx.com/" "Nginx" >}}.
+The configuration of Nginx is beyond the scope
+{{< web >}}
+of this series,
+{{< /web >}}
+{{< book >}}
+of this book,
+{{< /book >}}
+but there are plenty
+of solid tutorials
+that will show how to configure a Django app
+with Nginx.
+
+## Summary
+
+{{< web >}}
+In this article,
+{{< /web >}}
+{{< book >}}
+In this chapter,
+{{< /book >}}
+we covered static files.
+
+We looked at:
+
+* How to configure static files
+* The way to work with static files
+* How to handle static files
+ when deploying your site
+ to the internet
+
+{{< web >}}
+Looking ahead to the next article,
+{{< /web >}}
+{{< book >}}
+In the next chapter,
+{{< /book >}}
+we will learn about automated testing
+for your Django applications.
+Testing is one of my favorite topics
+so I'm excited to share with you
+about it.
+We'll cover:
+
+* Why would anyone want to write automated tests
+* What kinds of tests are useful
+ to a Django app
+* What tools can you use to make testing easier
+
+{{< web >}}
+If you'd like to follow along
+with the series,
+please feel free to sign up
+for my newsletter
+where I announce all of my new content.
+If you have other questions,
+you can reach me online
+on Twitter
+where I am
+{{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
+{{< /web >}}
+
diff --git a/content/understand-django/2021-02-22-test-your-apps.pt.md b/content/understand-django/2021-02-22-test-your-apps.pt.md
new file mode 100644
index 00000000..5704dd41
--- /dev/null
+++ b/content/understand-django/2021-02-22-test-your-apps.pt.md
@@ -0,0 +1,1142 @@
+---
+title: "Test Your Apps"
+description: >-
+ How do you confirm that your website works?
+ You could click around
+ and check things out yourself,
+ or you can write code to verify the site.
+ I'll show you why you should prefer the latter.
+ In this Understand Django article,
+ we'll study automated tests
+ to verify the correctness of your site.
+image: img/django.png
+type: post
+categories:
+ - Python
+ - Django
+tags:
+ - Python
+ - Django
+ - tests
+series: "Understand Django"
+
+---
+
+In the previous
+{{< web >}}
+[Understand Django]({{< ref "/understand-django/_index.md" >}})
+article,
+{{< /web >}}
+{{< book >}}
+chapter,
+{{< /book >}}
+we saw how static files
+like CSS, JavaScript, and images
+can be incorporated
+into your site.
+Now we're going to focus
+on how to verify
+that your website works
+and continues to work
+by writing automated tests
+that check your pages
+and your code logic.
+
+{{< understand-django-series "tests" >}}
+
+## Why Write Tests
+
+I'm going to assume
+that if you're reading this,
+then you've either got a Django project
+or are considering working
+with Django
+to build a project.
+If that's true,
+think about your project
+and how you would make sure it works.
+
+When you start out with a project,
+whether for a tutorial
+or for something real
+that you plan to grow,
+the fledgling site has very little functionality.
+To check that the site is working,
+you can start up the local web server,
+open your browser,
+navigate to the `localhost` URL,
+and confirm that the site is functional.
+How long does that take?
+5 seconds?
+15 seconds?
+30 seconds?
+
+For starting out,
+manually checking out your site is fine.
+What happens, though,
+when you create more pages?
+How do you continue to confirm
+that all your pages are functional?
+You could open up the local site
+and start clicking around,
+but the time spent confirming
+that everything works begins to grow.
+Maybe your verification effort takes 3 minutes, 5 minutes, or perhaps much more.
+If you're not careful,
+your creation may start to feel
+like the mythical multi-headed Hydra,
+and what once was a fun project
+to work on
+devolves into a chore
+of tedious page verification.
+
+You can't eliminate the fact
+that a larger project means
+that there is more to check.
+What you *can* do is change the name
+of the game.
+You can change your page checking
+from something manual
+that may take 15 seconds to verify a page
+to something that a computer can do
+in *milliseconds*.
+
+This is where automated tests come
+into the picture.
+Automated tests let computers do what computers do best:
+run repetitive tasks repeatedly, consistently,
+and quickly.
+When we write tests,
+our goal is to confirm some logic or behavior
+in a deterministic way.
+
+Let's look at a test
+for a hypothetical `add` function
+which functions
+like the `+` operator.
+This should give us a feel
+for what an automated test is like
+if you've never encountered tests before.
+
+```python
+def test_does_it_add():
+ assert add(40, 2) == 42
+```
+
+The test works by running the code
+and comparing the result
+to whatever we expect that result to be.
+The test *asserts* that the equality statement is true.
+If the equality is false,
+then the assertion raises an exception
+and the test fails.
+
+This automated test would take virtually no time to run
+if you compared it to running the function
+in a Python REPL
+to inspect the result manually.
+
+Seeing a silly example
+of an `add` function doesn't really help you much
+with how you should test your Django project.
+Next,
+we'll look at some types
+of tests
+for Django.
+If you add these kinds of tests
+to your project,
+you'll be able to make changes
+to your website
+with more confidence
+that you're not breaking things.
+
+## Useful Types Of Django Tests
+
+When we explored the anatomy
+of a Django application,
+I noted that I *always* delete the `tests.py` file
+that comes with the `startapp` command.
+The reason I do this is
+because there are different kinds
+of tests,
+and I want those different kinds
+to live in separate files.
+My apps have those separate files
+in a `tests` package
+within the app
+instead of a `tests.py` module.
+
+My `tests` package will often mirror the structure
+of the application itself.
+The program which executes tests,
+which is called a "test runner,"
+typically expects to find tests
+in files that start with `test_`.
+The package often includes:
+
+* `test_forms.py`
+* `test_models.py`
+* `test_views.py`
+* etc.
+
+This structure hints
+at the kinds
+of tests
+that you'd write
+for your application,
+but I'll touch on specifics more a bit later.
+Broadly,
+when we write automated tests,
+there is an important dimension
+to consider:
+how much application code should my test run?
+
+The answer to that question influences the behavior
+of tests.
+If we write a test
+that runs a lot of code,
+then we benefit
+by checking a lot of a system
+at once; however,
+there are some downsides:
+
+* Running a lot of code means more things can happen
+ and there is a higher chance
+ of your test breaking
+ in unexpected ways.
+ A test that often breaks
+ in unexpected ways is called a "brittle" test.
+* Running a lot of code means that there is a lot
+ of code to run.
+ That's axiomatic,
+ but the implication is that a test
+ with more code to execute will take longer
+ to run.
+ Big automated tests are still very likely
+ to be much faster
+ than the same test executed manually,
+ so running time is relative.
+
+When we have tests
+that run many parts
+of your application
+that are *integrated* together,
+we call these tests *integration tests*.
+Integration tests are good at surfacing issues related
+to the *connections between code*.
+For instance,
+if you called a method
+and passed in the wrong arguments,
+an integration test is likely
+to discover that problem.
+
+On the other end of the spectrum
+are tests that run very little code.
+The `add` test from above is a good example.
+These kinds of tests check individual units
+of code
+(e.g., a Django model).
+For that reason,
+we call these *unit tests*.
+Unit tests are good at *checking a piece
+of code
+in isolation*
+to confirm its behavior.
+
+Unit tests have downsides too.
+These tests execute
+without a lot of context
+from the rest
+of an application.
+This can help you confirm the behavior
+of the piece,
+but it might not be the behavior
+that the larger application requires.
+
+In this explanation,
+the lesson is that both kinds
+of tests are good,
+yet have tradeoffs.
+Beware of anyone who tells you
+that you should only write one kind
+of test
+or the other.
+
+> A good set of automated tests will include both **unit** and **integration** tests
+to check behavior
+of the individual units
+and the interconnections
+between parts.
+
+We have to consider another aspect
+to this discussion:
+what is the "right" amount
+of code for a unit test?
+*There's no absolutely correct answer here.*
+In fact,
+this topic is hotly debated
+among testers.
+
+Some people will assert
+that a unit test should only run the code
+for that unit.
+If you have a class
+that implements some pure logic
+and doesn't need other code,
+then you're in the ideal case.
+But what happens if you're testing
+a method that you added to a Django model
+that needs to interact
+with a database?
+Even if the only thing you're testing is the individual model method,
+a unit test purist would highlight
+that the test is actually an integration test
+if it interacts with a database.
+
+**I usually find this kind of discussion counterproductive.**
+In my experience,
+this sort of philosophical debate
+about what is a unit test doesn't typically help
+with testing your web app
+to verify its correctness.
+I brought all of this up because,
+if you're going to learn more about testing
+{{< web >}}
+after this article,
+{{< /web >}}
+{{< book >}}
+after this chapter,
+{{< /book >}}
+I caution you
+to avoid getting sucked
+into this definition trap.
+
+Here are my working definitions
+of unit and integration tests
+in Django.
+These definitions are imperfect
+(as are *any* definitions),
+but they should help frame the discussion
+{{< web >}}
+in this article.
+{{< /web >}}
+{{< book >}}
+in this chapter.
+{{< /book >}}
+
+* **Unit tests** - Tests that check individual units
+ within a Django project like a model method
+ or a form.
+* **Integration test** - Tests that check a group of units
+ and their interactions
+ like checking if a view renders the expected output.
+
+Now that we have some core notion
+of what tests are about,
+let's get into the details.
+
+### Unit Tests
+
+As we get into some examples,
+I need to introduce a couple
+of tools
+that I use
+on all of my Django projects.
+I'll describe these tools
+in more depth
+in a later section,
+but they need a brief introduction here
+or my examples won't make much sense.
+My two "must have" packages are:
+
+* `pytest-django`
+* `factory-boy`
+
+`pytest-django` is a package
+that makes it possible
+to run Django tests
+through the `pytest` program.
+pytest is an extremely popular Python testing tool
+with a huge ecosystem
+of extensions.
+In fact,
+`pytest-django` is one
+of those extensions.
+
+My biggest reason
+for using `pytest-django` is
+that it lets me use the `assert` keyword
+in all of my tests.
+In the Python standard library's `unittest` module
+and, by extension,
+Django's built-in test tools
+which subclass `unittest` classes,
+checking values requires methods
+like `assertEqual`
+and `assertTrue`.
+As we'll see,
+using the `assert` keyword exclusively is a very natural way
+to write tests.
+
+The other vital tool
+in my tool belt is `factory-boy`.
+PyPI calls this `factory-boy`,
+but the documentation uses `factory_boy`,
+so we'll use that naming from here on.
+`factory_boy` is a tool
+for building test database data.
+The library has fantastic Django integration
+and gives us the ability
+to generate model data
+with ease.
+
+Again,
+I'll focus on these two packages later on
+to cover more of their features,
+but you'll see them used immediately
+in the examples.
+
+#### Model Tests
+
+In Django projects,
+we use models
+to hold data
+about our app,
+so it's very natural
+to add methods
+to the models
+to interact
+with the data.
+How do we write a test
+that checks that the method
+does what we expect?
+
+I'm going to give you a mental framework
+for *any* of your tests,
+not only unit tests.
+This framework should help you reason
+through any tests
+that you encounter
+when reading and writing code.
+The framework is the *AAA pattern*.
+The AAA patterns stands for:
+
+* **Arrange** - This is the part of the test
+ that sets up your data
+ and any necessary preconditions
+ for your test.
+* **Act** - This stage is when your test runs the application code
+ that you want to test.
+* **Assert** - The last part checks
+ that your action is what you expected.
+
+For a model test,
+this looks like:
+
+```python
+# application/tests/test_models.py
+
+from application.models import Order
+from application.tests.factories import OrderFactory
+
+class TestOrder:
+ def test_shipped(self):
+ """After shipping an order, the status is shipped."""
+ order = OrderFactory(
+ status=Order.Status.PENDING
+ )
+
+ order.ship()
+
+ order.refresh_from_db()
+ assert order.status == Order.Status.SHIPPED
+```
+
+We can imagine a project
+that includes an ecommerce system.
+A big part
+of handling orders is tracking status.
+We could manually set the status field
+throughout the app,
+but changing status
+within a method gives us the chance
+to do other things.
+For instance,
+maybe the `ship` method also triggers sending an email.
+
+In the test above,
+we're checking the state transition
+from `PENDING` to `SHIPPED`.
+The test acts on the `ship` method,
+then refreshes the model instance
+from the database
+to ensure that the `SHIPPED` status persisted.
+
+What are some good qualities
+about this test?
+
+The test includes a docstring.
+Trust me, you *will* benefit from docstrings
+on your tests.
+There is a strong temptation to leave things
+at `test_shipped`,
+but in the future you may not have enough context.
+
+Many developers opt for long test names instead.
+While I have no problem with long descriptive test names,
+docstrings are helpful too.
+Whitespace is a *good* thing
+and, in my opinion, it's easier to read
+"The widget updates the game state when pushed."
+than `test_widget_updates_game_state_when_pushed`.
+
+The test checks one action.
+A test that checks a single action can fit
+in your head.
+There's no question about interaction
+with other parts.
+There's also no question about what is actually being tested.
+The simplicity of testing a single action makes each unit test
+tell a unique story.
+
+Conversely,
+you'll likely encounter tests
+in projects
+that do a lot of initial arrangement,
+then alternate between act and assert lines
+in a single test.
+These kinds of tests are brittle
+(i.e., the term to indicate that the test can break
+and fail easily)
+and are difficult to understand
+when there is a failure.
+
+The qualities in this test translate
+to lots of different test types.
+I think that's the beauty
+of having a solid mental model
+for testing.
+Once you see the way
+that tests:
+
+1. Set up the inputs,
+2. Take action,
+3. Check the outputs,
+
+then automated testing becomes a lot less scary
+and more valuable to you.
+Now let's see how this same pattern plays out
+in forms.
+
+#### Form Tests
+
+When writing tests,
+we often want to write a "happy path" test.
+This kind of test is when everything works exactly
+as you hope.
+This is a happy path form test.
+
+```python
+# application/tests/test_forms.py
+
+from application.forms import SupportForm
+from application.models import SupportRequest
+
+class TestSupportForm:
+ def test_request_created(self):
+ """A submission to the support form creates a support request."""
+ email = "hello@notreal.com"
+ data = {
+ "email": email,
+ "message": "I'm having trouble with your product."
+ }
+ form = SupportForm(data=data)
+ form.is_valid()
+
+ form.save()
+
+ assert SupportRequest.objects.filter(
+ email=email
+ ).count() == 1
+```
+
+With this test,
+we are synthesizing a POST request.
+The test:
+
+* Builds the POST data as `data`
+* Creates a bound form (i.e., connects `data=data` in the constructor)
+* Validates the form
+* Saves the form
+* Asserts that a new record was created
+
+Notice that I'm bending the AAA rules a bit
+for this test.
+Part of the Django convention
+for forms
+is that the form is valid
+before calling the `save` method.
+If that convention is not followed,
+then `cleaned_data` won't be populated correctly
+and most `save` methods depend on `cleaned_data`.
+Even though `is_valid` is an action,
+I view it as a setup step
+for form tests.
+
+When we work with forms,
+a lot of what we care about is cleaning the data
+to make sure
+that junk is not getting
+into your app's database.
+Let's write a test
+for an invalid form.
+
+```python
+# application/tests/test_forms.py
+
+from application.forms import SupportForm
+from application.models import SupportRequest
+
+class TestSupportForm:
+ # ... def test_request_created ...
+
+ def test_bad_email(self):
+ """An malformed email address is invalid."""
+ data = {
+ "email": "bogus",
+ "message": "Whatever"
+ }
+ form = SupportForm(data=data)
+
+ is_valid = form.is_valid()
+
+ assert not is_valid
+ assert 'email' in form.errors
+```
+
+The test shows the mechanics
+for checking an invalid form.
+The key elements are:
+
+* Set up the bad form data
+* Check the validity with `is_valid`
+* Inspect the output state in `form.errors`
+
+This test shows how to check an invalid form,
+but I'm less likely to write this particular test
+in a real project.
+Why?
+Because the test is checking functionality
+from Django's `EmailField`
+which has the validation logic
+to know what is a real email or not.
+
+Generally,
+I don't think it's valuable
+to test features
+from the framework itself.
+A good open source project
+like Django
+is already testing those features
+for you.
+When you write form tests,
+you should check on custom `clean_*` and `clean` methods
+as well as any custom `save` method
+that you might add.
+
+The patterns for both happy path and error cases are what I use
+for virtually all
+of my Django form tests.
+Let's move on
+to the integration tests
+to see what it looks like
+to test more code
+at once.
+
+### Integration Tests
+
+In my opinion,
+a good integration test won't look very different
+from a good unit test.
+An integration test can still follow the AAA pattern
+like other automated tests.
+The parts that change are the tools you'll use
+and the assertions you will write.
+
+My definition of an integration test
+in Django
+is a test
+that uses Django's test `Client`.
+{{< web >}}
+In previous articles,
+{{< /web >}}
+{{< book >}}
+In previous chapters,
+{{< /book >}}
+I've only mentioned what a client is in passing.
+In the context
+of a web application,
+a client is anything that consumes the output
+of a web app
+to display it to a user.
+
+The most obvious client for a web app
+is a web browser,
+but there are plenty
+of other client types out there.
+Some examples that could use output
+from a web application:
+
+* A native mobile application
+* A command line interface
+* A programming library like Python's `requests` package
+ that can handle HTTP requests and responses
+
+The Django test `Client` is like these other clients
+in that it can interact
+with your Django project
+to receive data from requests
+that it creates.
+The nice part about the test client is that the output
+is returned in a convenient way
+that we can assert against.
+The client returns the `HttpResponse` object directly!
+
+With that context,
+here's an integration test
+that we can discuss.
+
+```python
+# application/tests/test_views.py
+
+from django.test import Client
+from django.urls import reverse
+
+from application.tests.factories import UserFactory
+
+class TestProfileView:
+ def test_shows_name(self):
+ """The profile view shows the user's name."""
+ client = Client()
+ user = UserFactory()
+
+ response = client.get(
+ reverse("profile")
+ )
+
+ assert response.status_code == 200
+ assert user.first_name in response.content.decode()
+```
+
+What is this test doing?
+Also, what is this test *not* doing?
+
+By using the Django test client,
+the test runs a lot of Django code.
+This goes through:
+
+* URL routing
+* View execution (which will likely fetch from the database)
+* Template rendering
+
+That's a lot of code to execute
+in a single test!
+The goal of the test is
+to check that all the major pieces hang together.
+
+Now let's observe what the test is not doing.
+Even though the test runs a ton of code,
+there aren't a huge number of `assert` statements.
+In other words,
+our goal with an integration isn't
+to check every tiny little thing
+that could happen in the whole flow.
+Hopefully, we have unit tests
+that cover those little parts
+of the system.
+
+When I write an integration test,
+I'm mostly trying to answer the question:
+**does the *system* hold together without breaking?**
+
+Now that we've covered unit tests
+and integration tests,
+what are some tools that will help you make testing easier?
+
+## Tools To Help
+
+When testing your application,
+you have access to so many packages
+to help
+that it can be fairly overwhelming.
+If you're testing
+for the first time,
+you may be struggling
+with applying the AAA pattern
+and knowing what to test.
+We want to minimize the extra stuff
+that you have to know.
+
+We're going to revisit the tools
+that I listed earlier, `pytest-django` and `factory_boy`,
+to get you started.
+Consider these your Django testing survival kit.
+As you develop your testing skills,
+you can add more tools
+to your toolbox,
+but these two tools are a fantastic start.
+
+### `pytest-django`
+
+{{< extlink "https://docs.pytest.org/en/stable/" "pytest" >}} is a "test runner."
+The tool's job is to run automated tests.
+If you read {{< extlink "https://docs.djangoproject.com/en/4.1/topics/testing/overview/" "Writing and running tests" >}}
+in the Django documentation,
+you'll discover
+that Django *also* includes a test runner
+with `./manage.py test`.
+What gives?
+Why am I suggesting that you use `pytest`?
+
+I'm going to make a bold assertion:
+**pytest is better**.
+(Did I just go meta there? Yes, I did. 😆)
+
+I like a lot about Django's built-in test runner,
+but I keep coming back to pytest
+for one primary reason:
+I can use `assert` in tests.
+As you've seen
+in these test examples,
+the `assert` keyword makes for clear reading.
+We can use all of Python's normal comparison tests
+(e.g., `==`, `!=`, `in`)
+to check the output
+of tests.
+
+Django's test runner builds off the test tools
+that are included
+with Python
+in the `unittest` module.
+With those test tools,
+developers must make test classes
+that subclass `unittest.TestCase`.
+The downside of `TestCase` classes is
+that you must use a set of `assert*` methods
+to check your code.
+
+The list of `assert*` methods are included
+in the {{< extlink "https://docs.python.org/3/library/unittest.html#assert-methods" "unittest" >}} documentation.
+You can be very successful
+with these methods,
+but I think it requires remembering an API
+that includes a large number
+of methods.
+Consider this.
+Would you rather:
+
+1. Use `assert`? OR
+2. Use `assertEqual`,
+ `assertNotEqual`,
+ `assertTrue`,
+ `assertFalse`,
+ `assertIs`,
+ `assertIsNot`,
+ `assertIsNone`,
+ `assertIsNotNone`,
+ `assertIn`,
+ `assertNotIn`,
+ `assertIsInstance`,
+ and `assertNotIsInstance`?
+
+Using `assert` from pytest means
+that you get all the benefits
+of the `assert*` methods,
+but you only need to remember a single keyword.
+If that wasn't enough,
+let's compare the readability:
+
+```python
+self.assertEqual(my_value, 42)
+assert my_value == 42
+
+self.assertNotEqual(my_value, 42)
+assert my_value != 42
+
+self.assertIsNotNone(my_value)
+assert my_value is not None
+
+self.assertTrue(my_value)
+assert my_value
+```
+
+For the same reason
+that Python developers prefer `property` methods
+instead of getters and setters
+(e.g. `obj.value = 42` instead of `obj.set_value(42)`),
+I think the `assert` style syntax is far simpler
+to visually process.
+
+Outside of the awesome handling of `assert`,
+{{< extlink "https://pytest-django.readthedocs.io/en/latest/" "pytest-django" >}}
+includes a lot of other features
+that you might find interesting
+when writing automated tests.
+
+### `factory_boy`
+
+The other test package
+that I think every developer should use
+in their Django projects is
+`{{< extlink "https://factoryboy.readthedocs.io/en/stable/" "factory_boy" >}}`.
+
+> `factory_boy` helps you build model data for your tests.
+
+The recommendation when writing automated tests
+is to use an empty test database.
+If fact,
+the common pattern provided with Django testing tools
+is to use an empty database
+for every test.
+Having a blank slate in the database helps
+each test be independent
+and makes it easier to assert
+on the state of the database.
+Because the test database is empty,
+you'll need a strategy to populate your tests
+with the appropriate data to check against.
+
+As you build up your Django project,
+you will have more models
+that help to describe the domain
+that your website addresses.
+Generating model data
+for your tests is an
+immensely valuable capability.
+
+You *could* use your model manager's `create` method
+to create a database entry
+for your test,
+but you're going to run into some limits very fast.
+
+The biggest challenge with using `create` comes
+from database constraints like foreign keys.
+What do you do if you want to build a record
+that requires a large number
+of non-nullable foreign key relationships?
+Your only choice is to create those foreign key records.
+
+We can imagine an app
+that shows information about movies.
+The `Movie` model could have a variety
+of foreign key relationships
+like director, producer, studio,
+and so on.
+I'll use a few in the example,
+but imagine what would happen
+as the number of foreign key relationships increases.
+
+```python
+def test_detail_view_show_genre(client):
+ """The genre is on the detail page."""
+ director = Director.objects.create(
+ name="Steven Spielberg"
+ )
+ producer = Producer.objects.create(
+ name="George Lucas"
+ )
+ studio = Studio.objects.create(
+ name='Paramount'
+ )
+ movie = Movie.objects.create(
+ genre='Sci-Fi',
+ director=director,
+ producer=producer,
+ studio=studio
+ )
+
+ response = client.get(
+ reverse('movie:detail', args=[movie.id])
+ )
+
+ assert response.status_code == 200
+ assert 'Sci-Fi' in response.content.decode()
+```
+
+On the surface,
+the test isn't *too* bad.
+I think that's mostly because I kept the modeling simple.
+What if the `Director`, `Producer`, or `Studio` models also had required foreign keys?
+We'd spend most
+of our effort
+on the Arrangement section
+of the test.
+Also,
+as we inspect the test,
+we get bogged down with unnecessary details.
+Did we need to know the names
+of the director, producer, and studio?
+No, we didn't need that for this test.
+Now,
+let's look at the `factory_boy` equivalent.
+
+```python
+def test_detail_view_show_genre(client):
+ """The genre is on the detail page."""
+ movie = MovieFactory(genre='Sci-Fi')
+
+ response = client.get(
+ reverse('movie:detail', args=[movie.id])
+ )
+
+ assert response.status_code == 200
+ assert 'Sci-Fi' in response.content.decode()
+```
+
+`MovieFactory` seems like magic.
+Our test got to ignore all the other details.
+Now the test could focus entirely
+on the genre.
+
+Factories simplify the construction of database records.
+Instead of wiring the models together
+in the test,
+we move that wiring to the factory definition.
+The benefit is that our tests can use the plain style
+that we see in the second example.
+If we need to add a new foreign key
+to the model,
+only the factory has to be updated,
+not all your other tests
+that use that model.
+
+What might this `Movie` factory look like?
+The factory might be:
+
+```python
+# application/tests/factories.py
+
+import factory
+
+from application.models import Movie
+
+# Other factories defined here...
+
+class MovieFactory(factory.django.DjangoModelFactory):
+ class Meta:
+ model = Movie
+
+ director = factory.SubFactory(
+ DirectorFactory
+ )
+ producer = factory.SubFactory(
+ ProducerFactory
+ )
+ studio = factory.SubFactory(
+ StudioFactory
+ )
+ genre = 'Action'
+```
+
+This factory definition is very declarative.
+We declare what we want,
+and `factory_boy` figures out how to put it together.
+This quality leads to factories
+that you can reason about
+because you can focus
+on the what
+and not the how
+of model construction.
+
+The other noteworthy aspect is that the factories compose together.
+When we call `MovieFactory()`,
+`factory_boy` is missing data
+about everything
+so it must build all of that data.
+The challenge is that the `MovieFactory` doesn't know
+how to build a `Director`
+or any of the movie's foreign key relationships.
+Instead,
+the factory will delegate
+to *other* factories
+using the `SubFactory` attribute.
+By delegating to other factories,
+`factory_boy` can build the model
+and its entire tree
+of relationships
+with a single call.
+
+When we want to override the behavior
+of some of the generated data,
+we pass in the extra argument
+as I did in the second example
+by providing "Sci-Fi"
+as the `genre`.
+You can pass in other model instances
+to your factories too.
+
+`factory_boy` makes testing
+with database records a joy.
+In my experience,
+most of my Django tests require some amount
+of database data
+so I use factories very heavily.
+I think you will find
+that `factory_boy` is a worthy addition
+to your test tools.
+
+## Summary
+
+{{< web >}}
+In this article,
+{{< /web >}}
+{{< book >}}
+In this chapter,
+{{< /book >}}
+we explored tests
+with Django projects.
+We focused on:
+
+* Why anyone would want to write automated tests
+* What kinds of tests are useful
+ to a Django app
+* What tools you can use to make testing easier
+
+{{< web >}}
+Next time,
+{{< /web >}}
+{{< book >}}
+In the next chapter,
+{{< /book >}}
+we will dig into deployment.
+Deployment is getting your project
+into the environment
+where you will share your application
+for use.
+This might be the internet
+or it might be a private network
+for your company.
+Wherever you're putting your app,
+you'll want to know about:
+
+* Deploying your application with a Python web application server
+ (i.e., `./manage.py runserver` isn't meant for deployed apps)
+* Deployment preconditions
+ for managing settings, migrations, and static files
+* A checklist to confirm that your settings are configured
+ with the proper security guards
+* Monitoring your application for errors
+
+{{< web >}}
+If you'd like to follow along
+with the series,
+please feel free to sign up
+for my newsletter
+where I announce all of my new content.
+If you have other questions,
+you can reach me online
+on Twitter
+where I am
+{{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
+{{< /web >}}
+
diff --git a/content/understand-django/2021-03-23-deploy-site-live.pt.md b/content/understand-django/2021-03-23-deploy-site-live.pt.md
new file mode 100644
index 00000000..31d64bbd
--- /dev/null
+++ b/content/understand-django/2021-03-23-deploy-site-live.pt.md
@@ -0,0 +1,816 @@
+---
+title: "Deploy A Site Live"
+description: >-
+ You're ready to take the site you developed
+ and share it with the world.
+ What steps should you take
+ to prepare your Django project
+ for life on the web?
+ That's the focus
+ of this article.
+image: img/django.png
+type: post
+categories:
+ - Python
+ - Django
+tags:
+ - Python
+ - Django
+ - deployment
+series: "Understand Django"
+
+---
+
+In the previous
+{{< web >}}
+[Understand Django]({{< ref "/understand-django/_index.md" >}})
+article,
+{{< /web >}}
+{{< book >}}
+chapter,
+{{< /book >}}
+we looked at automated testing
+and how writing tests
+to check your Django project
+can be very valuable,
+saving you time
+and making sure your site works
+for your users.
+Next,
+we're going to look into
+how to share your site
+on the internet
+by understanding what it means
+to *deploy* a Django project.
+Deployment is the act
+of making your application live
+to your audience,
+{{< web >}}
+and this article explains the actions
+{{< /web >}}
+{{< book >}}
+and this chapter explains the actions
+{{< /book >}}
+you should consider
+to deploy effectively.
+
+{{< understand-django-series "deployment" >}}
+
+## Pick A Python Application Server
+
+When you begin to learn Django,
+the documentation will instruct you
+to use `./manage.py runserver`
+to interact with your application locally.
+`runserver` is a great tool
+for getting started
+because you can avoid extra software
+from the outset
+of your Django journey.
+
+While great,
+the `runserver` command is not designed
+for handling a lot of web traffic.
+`runserver` is explicitly intended
+for a development-only setting.
+Aside from a lack of performance tuning options,
+the server doesn't receive the same security scrutiny
+as other Python web application servers.
+
+These factors add up
+to make the `runserver` command unsuitable
+for handling your live site.
+What should you use instead?
+When you read the {{< extlink "https://docs.djangoproject.com/en/4.1/howto/deployment/wsgi/" "deployment documentation" >}},
+you'll find many possible Python web application servers listed.
+Gunicorn, uWSGI, Apache with mod_wsgi, Daphne, Hypercorn, and Uvicorn
+are all presented as available options.
+That's way too much choice.
+
+> Use {{< extlink "https://gunicorn.org/" "Gunicorn" >}}.
+
+Gunicorn (which stands for "Green Unicorn") is a very simple web application server
+to start using.
+In my experience,
+Gunicorn *stays* easy to use
+and works for projects that receive a ton of traffic.
+I've used some of the other options presented
+for Django apps,
+and few are as simple
+to use as Gunicorn.
+
+To use Gunicorn,
+we need to point the `gunicorn` command
+to the WSGI application
+that Django projects have.
+If you recall,
+WSGI is the Web Server Gateway Interface.
+WSGI is the protocol that permits Django apps
+to talk to any of these web application servers.
+
+If you ran the `startproject` command
+and named your Django project as "project,"
+the WSGI application should be in a file
+like `project/wsgi.py`.
+Gunicorn is aware that Django conventionally calls
+the WSGI application in that module `application`,
+so your only action is to point Gunicorn
+to the module
+with Python's dotted syntax.
+Here's the most basic setup.
+
+```bash
+$ gunicorn project.wsgi
+```
+
+Gunicorn works by starting a main process
+that will listen
+for HTTP requests
+on the local machine
+at port 5000
+by default.
+As each request reaches the main process,
+the request routes to an available worker process.
+The worker process executes your Django app code
+to provide the response data
+to the user.
+
+By default,
+Gunicorn will only create a single worker process.
+The Gunicorn documentation recommends picking a number
+that is two to four times larger
+than the number
+of CPU cores available
+to your machine.
+
+The number of workers is a large determining factor
+in how many requests
+your Django app can handle
+at once.
+The number of requests processed is usually called *traffic*
+by web developers.
+The idea of handling more traffic
+by creating more processes (i.e., Gunicorn workers)
+is called *horizontal scaling*.
+In contrast,
+*vertical scaling* handles more traffic
+by using a better individual computer.
+A faster processor with a single CPU can handle more requests.
+When thinking about performance,
+horizontal scaling is often a far easier approach.
+
+One of my projects
+has a small amount of traffic
+and runs on a single CPU
+on its hosting provider.
+In that scenario,
+I use two workers
+which looks like:
+
+```bash
+$ gunicorn project.wsgi --workers 2
+```
+
+The only other option you may require is an option
+to handle where logging data goes.
+I haven't covered logging in depth yet,
+{{< web >}}
+but recall from previous articles
+{{< /web >}}
+{{< book >}}
+but recall from previous chapters
+{{< /book >}}
+that logging allows you to record information
+about what your application is doing
+while it's running.
+
+Some hosting providers expect monitoring output
+like logging
+to go to stdout or stderr.
+stdout stands for "standard output"
+and stderr is "standard error."
+stdout is where data appears
+in your terminal
+when you use `print`.
+To tell Gunicorn to log
+to stderr,
+you can use a dash as the value
+to the `log-file` option.
+My full Gunicorn command looks like:
+
+```bash
+$ gunicorn project.wsgi \
+ --workers 2 \
+ --log-file -
+```
+
+A note about ASGI:
+I am assuming that your use of Django will use WSGI
+and its synchronous mode.
+In recent years,
+Django added support for asynchronous Python.
+Asynchronous Python brings the promise
+of higher performance
+with the tradeoff of some implementation complexity.
+For learning Django initially,
+you don't need to understand asynchronous Python
+and the Asynchronous Server Gateway Interface (ASGI).
+
+## Pick Your Cloud
+
+Once you know which application server to use
+and how to use it,
+you need to run your code somewhere.
+Again,
+you can be paralyzed
+by the sheer volume
+of choices available to you.
+AWS, GCP, Azure, Digital Ocean, Linode, Heroku, PythonAnywhere,
+and so many other cloud vendors are out there
+and able to run your application.
+
+If you're getting started,
+use a Platform as a Service (PaaS) option.
+Specifically,
+I think Heroku is a great platform
+for applications.
+A PaaS removes loads
+of operational complexity
+that you may be unequipped
+to handle initially
+if you're newer to web development.
+
+Think you want to run your application
+on a general cloud provider like AWS?
+You can certainly do that,
+but you'll potentially need to be prepared for:
+
+* Setting up machines
+* Getting TLS certificates for https
+* Doing database backups
+* Using configuration management tools to automate deployment
+* And loads more!
+
+You may be using Django to learn those skills.
+If so,
+that's awesome and good luck!
+But if your primary goal is to get your application out
+into the world,
+then these tasks are a huge drag
+on your productivity.
+
+Let's contrast this
+with Heroku.
+Because Heroku is a PaaS,
+they deal with the vast majority
+of the setup and coordination
+of machines in their cloud.
+Your experience as a developer primarily moves
+to a single command:
+
+```bash
+$ git push heroku main
+```
+
+The Heroku instructions have you set up a Git remote.
+That remote is the place you push your code to
+and let Heroku handle the deployment
+of your application.
+Heroku manages this by cleverly detecting Django applications
+and applying the correct commands.
+We'll see some of those required commands
+when looking at the preconditions.
+
+To be really clear,
+this is not an ad for Heroku.
+I have personal experience with most of the cloud vendors
+that I listed earlier
+in this section.
+I have found that a PaaS like Heroku is far and away an easier option
+to apply for my own projects.
+That's why I recommend the service so strongly.
+
+## Project Preconditions
+
+Django has a few preconditions
+that it expects
+before running your application
+in a live setting.
+{{< web >}}
+If you've read the previous articles,
+then you've actually seen most
+of these preconditions by now,
+{{< /web >}}
+{{< book >}}
+You've seen most
+of these preconditions by now
+in earlier chapters,
+{{< /book >}}
+but we'll group them together in this section
+so you can see the complete picture.
+
+One that we haven't discussed
+is the `DJANGO_SETTINGS_MODULE` environment variable.
+This is one critical element
+to your application
+because the variable signals
+to your Django application
+where the settings module is located.
+If you have different settings modules
+for different configurations
+(e.g., a live site configuration versus a unit testing configuration),
+then you may need to specify
+which settings module
+Django should use when running.
+
+{{< web >}}
+In a future article,
+{{< /web >}}
+{{< book >}}
+In a future chapter,
+{{< /book >}}
+we'll focus on how to manage your settings modules.
+At that time,
+you'll see how using some particular techniques diminish the need
+for multiple modules.
+For now,
+keep in mind `DJANGO_SETTINGS_MODULE` for deployments,
+and you should be good.
+
+The next important precondition
+for your app is keeping your database
+in sync using migrations.
+As mentioned
+{{< web >}}
+in the models article,
+{{< /web >}}
+{{< book >}}
+in the models chapter,
+{{< /book >}}
+we make migrations
+when making model changes.
+These migrations generate instructions
+for your relational database,
+so that Django can match the database schema.
+
+Without the migration system,
+Django would be unable to communicate effectively
+with the database.
+Because of that,
+you need to ensure
+that you have applied all migrations
+to your application
+before running your app.
+
+For whatever cloud you're using,
+you need to make sure
+that when you deploy,
+your deployment scripts run:
+
+```bash
+$ ./manage.py migrate
+```
+
+For instance,
+with my Heroku setup,
+Heroku lets me define a "release" command
+that they guarantee to run
+before launching the new version
+of the app.
+Heroku uses a `Procfile` to set
+which machines and commands to run
+so my `Procfile` looks like:
+
+```yaml
+release: python manage.py migrate
+web: gunicorn project.wsgi --workers 2 --log-file -
+```
+
+This file tells Heroku
+to run migrations before launching,
+then run gunicorn
+as the web process
+for the application.
+
+Another precondition needed
+for your app
+is static files.
+{{< web >}}
+We saw in the static files article
+{{< /web >}}
+{{< book >}}
+We saw in the static files chapter
+{{< /book >}}
+that Django looks
+for static files
+in a single directory
+for performance reasons.
+That requires running a command
+to put those files
+in the expected location.
+
+```bash
+$ ./manage.py collectstatic
+```
+
+In my deployment process
+for Heroku,
+this is a step that Heroku automatically does
+because it can detect a Django project.
+
+These items are the required preconditions
+to run your application.
+Django also has steps
+that aren't strictly required
+to make your app work,
+but are very beneficial.
+Let's look at how to address those next.
+
+## Protecting Your Site
+
+"Put on your seat belt."
+The average person knows
+that it's wise
+to wear a seat belt
+in a car.
+The statistical data is overwhelming
+that a seat belt can help save your life
+if you're ever
+in a car accident.
+Yet,
+a seat belt is not strictly necessary
+(aside from a legal perspective)
+to operate a vehicle.
+
+Django includes a command
+that produces a set of instructive safety messages
+for important site settings and configurations.
+Thankfully,
+ignoring these messages is unlikely
+to affect your personal health,
+but the messages are valuable
+to help you combat the bad forces
+that exist
+on the public internet.
+
+To view these important messages,
+run:
+
+```bash
+$ ./manage.py check --deploy --fail-level WARNING
+```
+
+On a little sample project
+that I created,
+{{< web >}}
+the (slightly reformatted for the article) output looks like:
+{{< /web >}}
+{{< book >}}
+the output looks like:
+{{< /book >}}
+
+```bash
+$ ./manage.py check --deploy --fail-level WARNING
+SystemCheckError: System check identified some issues:
+
+WARNINGS:
+?: (security.W004) You have not set a value for the SECURE_HSTS_SECONDS
+ setting. If your entire site is served only over SSL, you may want to
+ consider setting a value and enabling HTTP Strict Transport Security.
+ Be sure to read the documentation first; enabling HSTS carelessly can
+ cause serious, irreversible problems.
+?: (security.W008) Your SECURE_SSL_REDIRECT setting is not set to True.
+ Unless your site should be available over both SSL and non-SSL connections,
+ you may want to either set this setting True or configure a load balancer
+ or reverse-proxy server to redirect all connections to HTTPS.
+?: (security.W012) SESSION_COOKIE_SECURE is not set to True. Using a
+ secure-only session cookie makes it more difficult for network traffic
+ sniffers to hijack user sessions.
+?: (security.W016) You have 'django.middleware.csrf.CsrfViewMiddleware'
+ in your MIDDLEWARE, but you have not set CSRF_COOKIE_SECURE to True.
+ Using a secure-only CSRF cookie makes it more difficult for network traffic
+ sniffers to steal the CSRF token.
+?: (security.W018) You should not have DEBUG set to True in deployment.
+?: (security.W020) ALLOWED_HOSTS must not be empty in deployment.
+
+System check identified 6 issues (0 silenced).
+```
+
+The items reported by the checklist are often about settings
+that could be configured better.
+These checks are created
+by the {{< extlink "https://docs.djangoproject.com/en/4.1/topics/checks/" "System check framework" >}}
+that comes with Django.
+
+You should review each of the checks
+and learn about the changes
+that the check recommends.
+The items that appear with the `--deploy` flag are usually quite important
+and fixing them can greatly improve the safety
+and security
+of your application.
+
+Some of these checks are too modest about their importance.
+For instance, `security.W018` is the warning
+that tells you that `DEBUG` is set to `True`
+in the settings.
+`DEBUG = True` is *TERRIBLE*
+for a live site
+since it can *trivially* leak loads of private data.
+
+As a *warning*,
+`security.W018` will *not* fail the deploy check
+because `./manage.py check` defaults
+to failing
+on things that are errors.
+If you want to make sure
+that your site is sufficiently protected,
+I strongly encourage you
+to add the `--fail-level WARNING` flag
+so that the check will give those warnings the weight
+that they likely deserve.
+
+What do you do if the check is handled
+by some other part
+of your system?
+For example,
+maybe you've set up a secure configuration
+with HTTPS,
+and you've set HSTS headers
+with a reverse proxy
+like Nginx
+(this was one of the configurations
+that I mentioned
+{{< web >}}
+in the static files article).
+{{< /web >}}
+{{< book >}}
+in the static files chapter).
+{{< /book >}}
+If HSTS is handled elsewhere,
+you could set the `SILENCED_SYSTEM_CHECKS` setting
+to tell Django
+that you took care of it.
+
+```python
+# project/settings.py
+
+SILENCED_SYSTEM_CHECKS = [
+ "security.W004"
+]
+```
+
+Once you have finished the checklist,
+your application will be much better equipped
+to handle the hostile internet,
+but things can still go wrong.
+What should you do about errors
+that happen on your live site?
+Let's look at that next.
+
+## Prepare For Errors
+
+If an error happens on a live site
+and the site administrator (i.e., *you*) didn't hear it,
+did it really happen?
+**Yes, yes it did.**
+
+Dealing with a live site brings a new set
+of challenges.
+Try as we might to consider every possible action
+that our users do,
+we'll never get them all.
+There are lots
+of ways
+that a site can have errors
+from things that we failed
+to consider.
+Since errors *will* happen
+with a large enough product
+and large enough customer base,
+we need some plan to manage them.
+
+We can consider a few strategies:
+
+#### 1. Do nothing.
+
+While I don't recommend this strategy,
+you *could* wait for your customers
+to report errors to you.
+Some portion of customers might actually write to you
+and report a problem,
+but the vast majority won't.
+What's worse is that some
+of these customers may abandon your product
+if the errors are bad enough or frequent enough.
+
+> Using your customers to learn about errors makes for a poor user experience.
+
+#### 2. Use error emails.
+
+The Django deployment documentation highlights Django's ability
+to send error information
+to certain email addresses.
+*I don't recommend this strategy either.*
+Why?
+
+* Setting up email properly can be a very tricky endeavor
+ that involves far more configuration
+ than you may realize.
+ You may need email for your service,
+ but setting it up for error info alone is overkill.
+* The error emails can include Python tracebacks to provide context,
+ but other tools can provide much richer context information
+ (e.g., the browser used when a customer experiences an error).
+* If you have a runaway error
+ that happens constantly on your site,
+ you can say "bye, bye"
+ to your email inbox.
+ A flood of emails is a quick way to get email accounts flagged
+ and hurt the deliverability of email.
+
+This brings us to the final strategy
+that I'll cover.
+
+#### 3. Use an error tracking service.
+
+Error tracking services are specifically designed
+to collect context about errors,
+aggregate common errors together,
+and generally give you tools
+to respond to your site's errors appropriately.
+
+**I find that error tracking services are the best tools
+to understand what's going wrong
+on your live site**
+because the tools are purpose-built
+for detailing error behavior.
+
+Many of these services are not complicated
+to get installed and configured,
+and the services often have an extremely generous free tier
+to monitor your application.
+
+In the Django world,
+I generally hear about two
+of these error tracking services:
+{{< extlink "https://rollbar.com/" "Rollbar" >}}
+and {{< extlink "https://sentry.io/welcome/" "Sentry" >}}.
+I've used both of these error trackers,
+and I think they are both great.
+For my personal projects,
+I happen to pick Rollbar
+by default,
+so I'll describe
+that service
+in this section
+as an example.
+
+The flow for installing Rollbar is:
+
+1. Create a Rollbar account on their site.
+2. Install the `rollbar` package.
+3. Set some settings in a `ROLLBAR` dictionary.
+
+That's it!
+
+My Rollbar configuration
+for one of my projects looks like:
+
+```python
+# project/settings.py
+
+ROLLBAR = {
+ "enabled": env("ROLLBAR_ENABLED"),
+ "access_token": env("ROLLBAR_ACCESS_TOKEN"),
+ "environment": env("ROLLBAR_ENVIRONMENT"),
+ "branch": "main",
+ "root": BASE_DIR,
+}
+```
+
+In this example,
+everything coming from `env` is from an environment variable
+that we'll discuss more when we focus on settings management
+in Django.
+
+The `enabled` parameter can quickly turn Rollbar on and off.
+This is good for local development
+so you're not sending data to the service when working on new features.
+
+The `access_token` is the secret that Rollbar will provide
+in your account
+to associate your app's error data
+with your account
+so you can see problems.
+
+The `environment` setting lets Rollbar split your errors
+into different groupings.
+You can use this to separate different configurations
+that you put on the internet.
+For instance,
+the software industry likes to call live sites "production."
+You may also have a separate site
+that is available privately
+to a team
+that you might call "development."
+
+The other settings tell Rollbar information
+that can help map errors back to your code repository.
+
+Once you set this up,
+how can you tell that it's working?
+Like a musician tapping a microphone
+to see if it's working,
+I like to add a view to my code
+that lets me test
+that my error tracking service is operational.
+
+```python
+# application/views.py
+
+from django.contrib.admin.views.decorators import staff_member_required
+
+@staff_member_required
+def boom(request):
+ """This is for checking error handling (like Rollbar)."""
+ raise Exception("Is this thing on?")
+```
+
+I connect this view to a URL configuration,
+then check it after I deploy Rollbar
+for the first time.
+Importantly,
+don't forget to include a `staff_member_required` decorator
+so that random people
+on the internet can't trigger errors
+on your server on a whim!
+
+With error tracking set up,
+you'll be in a good position
+to see errors when they happen
+on your site.
+In fact,
+a great way to win the favor
+of customers can be to fix errors proactively
+and reach out to them.
+Most people don't enjoy contacting support
+and would be surprised and delighted
+if you tell them
+that you fixed their problem immediately.
+
+## Summary
+
+{{< web >}}
+In this article,
+{{< /web >}}
+{{< book >}}
+In this chapter,
+{{< /book >}}
+we learned the things
+to consider when deploying a site
+to the internet.
+We examined:
+
+* Deploying your application with a Python web application server
+ (i.e., `./manage.py runserver` isn't meant for deployed apps)
+* Running your app on a cloud vendor
+* Deployment preconditions
+ for managing settings, migrations, and static files
+* A checklist to confirm that your settings are configured
+ with the proper security guards
+* Monitoring your application for errors
+
+{{< web >}}
+In the next article,
+{{< /web >}}
+{{< book >}}
+In the next chapter,
+{{< /book >}}
+we'll look at Django's tools
+for managing shorter term user data
+like authentication info
+with Django sessions.
+We'll see the different modes
+that Django provides
+and how to use sessions
+to support your project.
+You'll learn about:
+
+* What sessions are and how they work
+* Ways that Django uses sessions
+* How to use sessions in your apps
+
+{{< web >}}
+If you'd like to follow along
+with the series,
+please feel free to sign up
+for my newsletter
+where I announce all of my new content.
+If you have other questions,
+you can reach me online
+on Twitter
+where I am
+{{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
+{{< /web >}}
+
diff --git a/content/understand-django/2021-05-21-sessions.pt.md b/content/understand-django/2021-05-21-sessions.pt.md
new file mode 100644
index 00000000..01beba6f
--- /dev/null
+++ b/content/understand-django/2021-05-21-sessions.pt.md
@@ -0,0 +1,519 @@
+---
+title: "Per-visitor Data With Sessions"
+description: >-
+ How does Django know
+ when a user is logged in?
+ Where can the framework store data
+ for a visitor on your app?
+ In this article,
+ we'll answer those questions
+ and look at a storage concept
+ in Django
+ called sessions.
+image: img/django.png
+type: post
+categories:
+ - Python
+ - Django
+tags:
+ - Python
+ - Django
+ - sessions
+series: "Understand Django"
+
+---
+
+In the last
+{{< web >}}
+[Understand Django]({{< ref "/understand-django/_index.md" >}})
+article,
+{{< /web >}}
+{{< book >}}
+chapter,
+{{< /book >}}
+we saw
+what it takes
+to make your Django project live
+on the internet.
+Now,
+we'll get back to a more narrow topic
+and focus on a way Django can store data
+for visitors to your site.
+This is the kind of data
+that doesn't often fit well
+into your Django models
+and is called *session* data.
+
+{{< understand-django-series "sessions" >}}
+
+## What Is A Session?
+
+As *I* was learning Django,
+I'd run into sessions occasionally
+and accept that I didn't really understand them.
+They felt like magic to me.
+But what is a session?
+
+> A session is a set of data
+that is available to users
+that Django can use over multiple requests.
+
+From a development perspective,
+the data is different
+from the regular data
+that you would store in a database
+with Django models.
+When working with session data,
+you don't query the database
+using the ORM.
+Instead,
+you can access session content
+via the `request.session` attribute.
+
+The `request.session` is a dictionary-like object.
+Storing data into the session is like working
+with any other Python dictionary.
+
+```python
+# application/view.py
+
+from django.http import HttpResponse
+
+def a_session_view(request):
+ request.session['data_to_keep'] = 'store this'
+ return HttpReponse('')
+```
+
+When Django stores the session data,
+the framework will keep the data
+in a JSON format.
+What is JSON?
+I've mentioned JSON in passing
+{{< web >}}
+in previous articles,
+{{< /web >}}
+{{< book >}}
+in previous chapters,
+{{< /book >}}
+but now is a decent time to explain it.
+Knowing what JSON is will help you understand
+what happens to session data
+as the data is stored.
+
+### The "What is JSON?" Sidebar
+
+JSON is a data format.
+JSON is a way of describing data
+so that the data can be stored
+or transmitted.
+The definition of that format is listed
+on the official {{< extlink "https://www.json.org/json-en.html" "JSON website" >}}
+and can be understood
+in probably 10 minutes or less.
+
+That stored data can be parsed
+based on the definition of the format
+to recreate the data
+at a different time
+or on a different computer.
+In general,
+you can view JSON as a tool
+to take Python dictionaries or lists
+and store or transmit them
+for use elsewhere.
+
+The Python standard library includes a module
+for working with JSON data.
+Here's an example to give you an idea
+of what JSON output looks like.
+
+```python
+>>> import json
+>>> data = {'hello': 'world'}
+>>> json.dumps(data)
+'{"hello": "world"}'
+>>> json_string = json.dumps(data)
+>>> parsed_data = json.loads(json_string)
+>>> parsed_data
+{'hello': 'world'}
+>>> data == parsed_data
+True
+```
+
+The `dumps` and `loads` functions transform data
+to and from a string, respectively
+(the `s` in the those function names stands for "string").
+
+JSON is an extremely versatile format
+and is used all over the internet.
+Getting back to sessions,
+JSON is a good fit
+because there are multiple places
+where Django can store session data.
+Let's look at those next.
+
+## Session Storage
+
+{{< web >}}
+You probably know this drill by now
+if you've been following this series.
+{{< /web >}}
+{{< book >}}
+You probably know this drill by now.
+{{< /book >}}
+Like the template system,
+the ORM,
+and the authentication system,
+the session application is configurable
+with multiple different "engines"
+to store session data.
+
+When you start a new Django project
+with the `startproject` command,
+the session engine will be set
+to `django.contrib.sessions.backends.db`.
+This is because the `SESSION_ENGINE` setting will be unset
+in your settings module,
+and Django will fall back to the default.
+
+With this engine,
+Django will store session data
+in the database.
+Because `startproject` includes the `django.contrib.sessions` app
+in `INSTALLED_APPS`,
+you'd probably see the following stream by
+when you migrate your database
+for the first time.
+
+```text
+(venv) $ ./manage.py migrate
+ ...
+Running migrations:
+ ...
+ Applying sessions.0001_initial... OK
+```
+
+The `Session` model stores three things:
+
+* A session key that uniquely identifies the session
+ in the storage engine
+* The actual session data,
+ stored in JSON format,
+ in a `TextField`
+* An expiration date for the session data
+
+With these three fields,
+Django can handle the temporary storage needs
+for any of your site's visitors.
+
+Why is the session engine configurable?
+Django's session storage is configurable
+to manage tradeoffs.
+The default storage of a database engine
+is a safe default
+and the easiest to understand.
+The answer to "Where is my app's session data?"
+is "In the database with all of my other application data."
+
+If your site grows in popularity
+and usage,
+using the database to store sessions can become a bottleneck
+and limit your performance and application scaling.
+Additionally,
+the default engine creates an ever expanding set
+of database rows in the `Session` model's table.
+You can work around the second challenge
+by periodically running the `clearsessions` management command,
+but what if the performance is a problem for you?
+
+This is where other storage engines might be better
+for your application.
+One method to improve performance is
+to switch to an engine
+that uses caching.
+If you have set up the caching system
+with a technology
+like {{< extlink "https://redis.io/" "Redis" >}}
+or {{< extlink "https://memcached.org/" "Memcached" >}},
+then a lot of session load
+on the database
+can be pushed to the cache service.
+Caching is a topic we will explore more
+{{< web >}}
+in a future article,
+{{< /web >}}
+{{< book >}}
+in a future chapter,
+{{< /book >}}
+so if this doesn't make too much sense right now,
+I apologize for referencing concepts
+that I haven't introduced yet.
+For the time being,
+understand that caching can improve session performance.
+
+Another session storage engine
+that can remove load
+from a database
+uses the browser's cookie system.
+This system will certainly remove database load
+because the state will be stored
+with the browser,
+but this strategy comes
+with its own set
+of tradeoffs.
+With cookie-base storage:
+
+* The storage could be cleared at any time
+ by the user.
+* The storage engine is limited to a small amount
+ of data storage
+ by the browser,
+ based on the maximum allowed size
+ of a cookie
+ (commonly, only 4kB).
+
+Choosing the right session storage engine
+for your application depends
+on what the app does.
+If you're in doubt,
+start with the default of database-backed storage,
+and you should be fine initially.
+
+## How Does The Session System Identify Visitors?
+
+When a visitor comes to your site,
+Django needs to associate the session data
+to the visitor.
+To do this association,
+Django will store a session identifier
+in a cookie
+on the user's browser.
+
+On the first visit,
+the session storage engine will look
+for a cookie
+with the name `sessionid` (by default).
+If the application doesn't find that cookie,
+then the session storage will generate a random ID
+and ensure that the random ID doesn't conflict
+with any other session IDs
+that already exist.
+
+From there, the storage engine will store some session data
+via whatever mechanism that engine uses
+(e.g., the database engine will create a new session row
+in the table).
+
+The session ID is added
+to the user's browser cookies
+for your site's domain.
+Cookies are stored in a secured manner
+so only that browser will have access
+to that randomly generated value.
+The session ID is very long (32 characters),
+and the session will expire after a given length of time.
+These characteristics make session IDs quite secure.
+
+Since sessions are secure
+and can uniquely identify a browser,
+what kind of data can we put in there?
+
+## What Uses Sessions?
+
+Sessions can store all kinds of data,
+but what are some real world use cases?
+You can look
+in Django's source code
+to find some immediate answers!
+
+In my estimate,
+the most used part of Django
+that uses sessions heavily
+is the auth system.
+We explored the authentication and authorization system
+{{< web >}}
+in [User Authentication]({{< ref "/understand-django/2020-11-04-user-authentication.md" >}}).
+{{< /web >}}
+{{< book >}}
+in the User Authentication chapter.
+{{< /book >}}
+At the time,
+I mentioned
+in the pre-requisites
+that sessions were required,
+but I noted that sessions were an internal detail.
+Now that you know what sessions are about,
+let's see how the auth system uses them.
+
+If you look into the session data
+after you've authenticated,
+you'll find three pieces of information:
+
+* The user's ID (stored in `_auth_user_id`)
+* The user's hash (stored in `_auth_user_hash`)
+* The string name of the auth backend used
+ (stored in `_auth_user_backend`)
+
+Since we know that a session identifies a browser
+and does so securely,
+the auth system stores identity information
+into the session
+to tie that unique session to a unique user.
+When a user's browser makes an HTTP request,
+Django can determine the session associated
+with the request
+via the `sessionid`
+and gain access to the user auth data
+(i.e., the user's ID, hash, and auth backend).
+With these data elements,
+the auth system can determine
+if the request is valid
+and should be considered authenticated
+by checking against the associated auth session data.
+
+The auth system will read which backend is used
+and load that backend if possible.
+The backend is used to load the specific user record
+from the ID found
+in the session.
+Finally,
+that user is used to check
+if the hash provided validates
+when compared to the user's hashed password
+(there is some extra hashing involved
+to ensure that the user's password hash is not stored directly
+in the session).
+If the comparison checks out,
+the user is authenticated
+and the request proceeds as an authenticated request.
+
+You can see that the session is vital
+to this flow with the auth system.
+Without the ability to store state
+in the session,
+the user would be unable
+to prove who they were.
+
+Another use of sessions is found
+with CSRF handling.
+The CSRF security features in Django
+{{< web >}}
+(which I mentioned in the forms article
+{{< /web >}}
+{{< book >}}
+(which I mentioned in the forms chapter
+{{< /book >}}
+and we will explore more in a future topic)
+permit CSRF tokens to be stored
+in the session
+instead of a cookie
+when the `CSRF_USE_SESSIONS` setting is enabled.
+Django provides a safe default
+for CSRF tokens
+in cookies,
+but the session is an alternative storage place
+if you're not happy enough
+with the cookie configuration.
+
+As a final example,
+we can look
+at the `messages` application.
+The `messages` app can store "flash" messages.
+A flash message is the kind
+of message
+that you'd expect to see
+on a single page view.
+For instance,
+if you have a message
+that you'd like to display
+to a user upon some action,
+you might use a flash message.
+Perhaps your application has some "Contact Us" form
+to receive customer feedback.
+After the customer submits the form,
+you might want the application
+to flash "Thank you for the feedback!"
+
+```python
+# application/views.py
+
+from django.contrib import messages
+from django.views.generic import FormView
+from django.urls import reverse_lazy
+
+from .forms import ContactForm
+
+class ContactView(FormView):
+ form_class = ContactForm
+ success_url = reverse_lazy("application:index")
+
+ def form_valid(self, form):
+ messages.info(
+ self.request,
+ "Thank you for the feedback!"
+ )
+ return super().form_valid(form)
+```
+
+In the default setup,
+Django will attempt to store the flash message
+in the request's cookies,
+but, as we saw earlier,
+browsers constrain the maximum cookie size.
+If the flash messages will not fit in the request's cookies,
+then the `messages` app will switch
+to the session as a more robust alternative.
+Observe that this might run into problems
+if you are using the session's cookie storage engine!
+
+I hope that these examples from Django's `contrib` package provide you
+with some ideas for how you might use sessions
+in your own projects.
+
+## Summary
+
+{{< web >}}
+In this article,
+{{< /web >}}
+{{< book >}}
+In this chapter,
+{{< /book >}}
+we dug into Django sessions
+and how you use them.
+We saw:
+
+* What sessions are and the interface they expose as `request.session`
+* How JSON is used to manage session data
+* Different kinds of session storage
+ that are available to your site
+* The way that Django recognizes a user's session in the browser
+* Examples within `django.contrib`
+ of how sessions get used by Django's built-in apps.
+
+{{< web >}}
+In the next article,
+{{< /web >}}
+{{< book >}}
+In the next chapter,
+{{< /book >}}
+we are going to spend time focusing
+on settings in Django.
+You'll learn about:
+
+* Various strategies for managing your project's settings
+* Django's tools to help with settings
+* Tools in the larger Django ecosystem that can make your life easier
+
+{{< web >}}
+If you'd like to follow along
+with the series,
+please feel free to sign up
+for my newsletter
+where I announce all of my new content.
+If you have other questions,
+you can reach me online
+on Twitter
+where I am
+{{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
+{{< /web >}}
+
diff --git a/content/understand-django/2021-07-15-settings.pt.md b/content/understand-django/2021-07-15-settings.pt.md
new file mode 100644
index 00000000..4626e7ff
--- /dev/null
+++ b/content/understand-django/2021-07-15-settings.pt.md
@@ -0,0 +1,838 @@
+---
+title: "Making Sense Of Settings"
+description: >-
+ All Django apps need to be configured
+ in order to run properly.
+ In this article,
+ we will dig into how Django lets you configure your project
+ using a settings module.
+ We'll also look at ways to be extra effective
+ with settings.
+image: img/django.png
+type: post
+categories:
+ - Python
+ - Django
+tags:
+ - Python
+ - Django
+ - settings
+series: "Understand Django"
+
+---
+
+In the last
+{{< web >}}
+[Understand Django]({{< ref "/understand-django/_index.md" >}})
+article,
+{{< /web >}}
+{{< book >}}
+chapter,
+{{< /book >}}
+we looked at a storage concept
+in Django
+called sessions.
+Sessions provide a solution to problems
+like
+"How does Django know
+when a user is logged in?" or
+"Where can the framework store data
+for a visitor on your app?"
+
+{{< web >}}
+With this article,
+{{< /web >}}
+{{< book >}}
+With this chapter,
+{{< /book >}}
+you'll learn about Django settings
+and how to manage the configuration
+of your application.
+We'll also look at tools
+to help you
+define your settings effectively.
+
+{{< understand-django-series "settings" >}}
+
+## How Is Django Configured?
+
+To run properly,
+Django needs to be configured.
+We need to understand
+where this configuration comes from.
+Django has the ability
+to use default configuration values
+or values set
+by developers like yourself,
+but where does it get those from?
+
+Early in the process
+of starting a Django application,
+Django will internally import the following:
+
+```python
+from django.conf import settings
+```
+
+This `settings` import is a package level object
+created in `django/conf/__init__.py`.
+The `settings` object has attributes added to it
+from two primary sources.
+
+The first source is a set of global default settings
+that come from the framework.
+These global settings are from `django/conf/global_settings.py`
+and provide a set of initial values
+for configuration
+that Django needs to operate.
+
+The second source of configuration settings comes
+from user defined values.
+Django will accept a Python module
+and apply its module level attributes
+to the `settings` object.
+To find the user module,
+Django searches for a `DJANGO_SETTINGS_MODULE` environment variable.
+
+### Sidebar: Environment Variables
+
+Environment variables are not a Django concept.
+When any program runs
+on a computer,
+the operating system makes certain data available
+to the running program.
+This set of data is called the program's "environment,"
+and each piece of data
+in that set
+is an environment variable.
+
+If you're starting Django from a terminal,
+you can view the environment variables
+that Django will receive from the operating system
+by running the `env` command on macOS or Linux,
+or the `set` command on Windows.
+
+We can add our own environment variables
+to the environment
+with the `export` command on macOS or Linux,
+or the `set` command on Windows.
+Environment variables are typically named
+in all capital letters.
+
+```bash
+$ export HELLO=world
+```
+
+Now that we have a basic understanding
+of environment variables,
+let's return to the `DJANGO_SETTINGS_MODULE` variable.
+The variable's value should be the location
+of a Python module containing any settings
+that a developer wants to change
+from Django's default values.
+
+If you create a Django project
+with `startproject`
+and use `project` as the name,
+then you will find a generated settings file
+with the path `project/settings.py`.
+When Django runs,
+you could explicitly instruct Django with:
+
+```bash
+$ export DJANGO_SETTINGS_MODULE=project.settings
+```
+
+Instead of supplying the file path,
+the `DJANGO_SETTINGS_MODULE` should be
+in a Python module dotted notation.
+
+You may not actually need
+to set `DJANGO_SETTINGS_MODULE` explicitly.
+If you stick with the same settings file
+that is created by `startproject`,
+you can find a line
+in `wsgi.py`
+that looks like:
+
+```python
+os.environ.setdefault(
+ 'DJANGO_SETTINGS_MODULE',
+ 'project.settings'
+)
+```
+
+Because of this line,
+Django will attempt to read
+from `project.settings`
+(or whatever you named your project)
+without the need to explicitly set `DJANGO_SETTINGS_MODULE`.
+Feel free to adjust the default value
+if you have a different settings file
+that you prefer to use
+for local development.
+
+Once Django reads the global settings
+and any user defined settings,
+we can get any configuration
+from the `settings` object
+via attribute access.
+This convention of keeping all configuration
+in the `settings` object
+is a convenient pattern
+that the framework,
+third party library ecosystem,
+and *you* can depend on.
+
+```python
+$ ./manage.py shell
+>>> from django.conf import settings
+>>> settings.SECRET_KEY
+'a secret to everybody'
+```
+
+The `settings` object is a shared item
+so it is generally thought
+to be a Really Bad Idea™
+to edit and assign to the object directly.
+Keep your settings in your settings module!
+
+That's the core of Django configuration.
+We're ready to focus
+on the user defined settings
+and our responsibilities
+as Django app developers.
+
+## Settings Module Patterns
+
+There are multiple ways to deal
+with settings modules
+and how to populate those modules
+with the appropriate values
+for different environments.
+Let's look at some popular patterns.
+
+### Multiple Modules Per Environment
+
+A Django settings module is a Python module.
+Nothing is stopping us
+from using the full power
+of Python
+to configure that module the way we want.
+
+Minimally, you will probably have at least two environments
+where your Django app runs:
+
+* On your local machine while developing
+* On the internet for your live site
+
+We should know by now
+that setting `DEBUG = True` is a terrible idea
+for a live Django site,
+so how can we get the benefits of the debug mode
+without having `DEBUG` set to `True`
+in our module?
+
+One technique is to use separate settings modules.
+With this strategy,
+you can pick which environment your Django app should run for
+by switching the `DJANGO_SETTINGS_MODULE` value
+to pick a different environment.
+You might have modules like:
+
+* `project.settings.dev`
+* `project.settings.stage`
+* `project.settings.production`
+
+These examples would be for a local development environment
+on your laptop,
+a staging environment
+(which is a commonly used pattern
+for testing a site
+that is as similar to the live site as possible
+without *being* the live site),
+and a production environment.
+{{< web >}}
+As a reminder from the deployment article,
+{{< /web >}}
+{{< book >}}
+As a reminder from the deployment chapter,
+{{< /book >}}
+the software industry likes to call the primary site
+for customers "production."
+
+This strategy has certain challenges to consider.
+Should you replicate settings
+in each file
+or use some common module between them?
+
+If you decide to replicate the settings across modules,
+you'll have the advantage that the settings module shows *all*
+of the settings in a single place
+for that environment.
+The disadvantage is that keeping the common settings the same
+could be a challenge
+if you forget to update one of the modules.
+
+On the other hand,
+you could use a common module.
+The advantage to this form is that the common settings can be
+in a single location.
+The environment specific files only need to record the *differences*
+between the environments.
+The disadvantage is that it is harder to get a clear picture
+of all the settings of that environment.
+
+If you decide to use a common module,
+this style is often implemented
+with a `*` import.
+I can probably count on one hand the number
+of places
+where I'm ok with a `*` import,
+and this is one of them.
+In most cases the Python community prefers explicit over implicit,
+and the idea extends to the treatment of imports.
+Explicit imports make it clear what a module is actually using.
+The `*` import is very implicit,
+and it makes it unclear what a module uses.
+For the case of a common settings module,
+a `*` import is actually positive
+because we want to use *everything*
+in the common module.
+
+Let's make this more concrete.
+Assume that you have a `project.settings.base` module.
+This module would hold your common settings
+for your app.
+I'd recommend that you try to make your settings safe and secure
+by default.
+For instance,
+use `DEBUG = False` in the base settings
+and force other settings modules
+to opt-in
+to the more unsafe behavior.
+
+For your local development environment
+on your laptop,
+you could use `project.settings.dev`.
+This settings module would look like:
+
+```python
+# project/settings/dev.py
+
+from project.settings.base import *
+
+DEBUG = True
+
+# Define any other settings that you want to override.
+...
+```
+
+By using the `*` import in the `dev.py` file,
+all the settings from `base.py` are pulled
+into the module level scope.
+Where you want a setting to be different,
+you set the value in `dev.py`.
+When Django starts using `DJANGO_SETTINGS_MODULE`
+of `project.settings.dev`,
+all the values from `base.py` will be used
+via `dev.py`.
+
+This scheme gives you control to define common things once,
+but there is still a big challenge
+with this.
+What do we do about settings
+that need to be kept secret
+(e.g., API keys)?
+
+*Don't commit secret data to your code repository!*
+Adding secrets to your source control tool
+like Git
+is usually not a good idea.
+This is especially true
+if you have a public repository
+on GitHub.
+Think no one is paying attention to your repo?
+Think again!
+There are tools out there
+that scan *every public commit* made to GitHub.
+These tools are specifically looking
+for secret data
+to exploit.
+
+If you can't safely add secrets
+to your code repo,
+where can we add them instead?
+You can use environment variables!
+Let's look at another scheme
+for managing settings
+with environment variables.
+
+### Settings Via Environment Variables
+
+In Python,
+you can access environment variables
+through the `os` module.
+The module contains the `environ` attribute,
+which functions like a dictionary.
+
+By using environment variables,
+your settings module can get configuration settings
+from the external environment
+that is running the Django app.
+This is a solid pattern because it can accomplish two things:
+
+* Secret data can be kept out of your code
+* Configuration differences between environments
+ can be managed by changing environment variable values
+
+Here's an example of secret data management:
+
+```python
+# project/settings.py
+
+import os
+
+SECRET_KEY = os.environ['SECRET_KEY']
+
+...
+```
+
+Django needs a secret key
+for a variety of safe hashing purposes.
+There is a warning in the default `startproject` output
+that reads:
+
+```python
+# SECURITY WARNING: keep the secret key used in production secret!
+```
+
+By moving the secret key value
+to an environment variable
+that happens to have a matching name of `SECRET_KEY`,
+we won't be committing the value
+to source control
+for some nefarious actor to discover.
+
+This pattern works really well for secrets,
+but it can also work well
+for *any* configuration
+that we want to vary between environments.
+
+For instance,
+on one of my projects,
+I use the excellent {{< extlink "https://anymail.readthedocs.io/en/stable/" "Anymail" >}} package
+to send emails
+via an email service provider
+(of the ESPs, I happen to use {{< extlink "https://sendgrid.com/" "SendGrid" >}}).
+When I'm working with my development environment,
+I don't want to send real email.
+Because of that,
+I use an environment variable
+to set Django's `EMAIL_BACKEND` setting.
+This lets me switch between the Anymail backend
+and Django's built-in
+`django.core.mail.backends.console.EmailBackend`
+that prints emails to the terminal instead.
+
+If I did this email configuration with `os.environ`,
+it would look like:
+
+```python
+# project/settings.py
+
+import os
+
+EMAIL_BACKEND = os.environ.get(
+ "EMAIL_BACKEND",
+ "anymail.backends.sendgrid.EmailBackend"
+)
+
+...
+```
+
+I prefer to make my default settings closer
+to the live site context.
+This not only leads to safer behavior
+(because I have to explicitly opt-out
+of safer settings like switching from `DEBUG = False` to `DEBUG = True`),
+but it also means that my live site has less to configure.
+That's good because there are fewer chances
+to make configuration mistakes
+on the site that matters most:
+the one where my customers are.
+
+We need to be aware of a big gotcha
+with using environment variables.
+*Environment variables* are only available as a `str` type.
+This is something to be aware of
+because there will be times when you want a boolean settings value
+or some other *type* of data.
+In a situation
+where you need a different type,
+you have to coerce a `str`
+into the type you need.
+In other words,
+don't forget that every string
+except the empty string is truthy
+in Python:
+
+```python
+>>> not_false = "False"
+>>> bool(not_false)
+True
+```
+
+In the next section,
+we will see tools that help alleviate this typing problem.
+
+Note:
+As you learn more about settings,
+you will probably encounter advice
+that says to avoid using environment variables.
+This is well intentioned advice
+that highlights
+that there *is* some risk
+with using environment variables.
+With this kind of advice,
+you may read a recommendation
+for secret management tools
+like {{< extlink "https://www.vaultproject.io/" "HashiCorp Vault" >}}.
+These are good tools,
+but consider them a more advanced topic.
+In my opinion,
+using environment variables for secret management
+is a reasonably low risk storage mechanism.
+
+## Settings Management Tools
+
+We can focus on two categories
+of tools
+that can help you manage your settings
+in Django:
+built-in tools and third party libraries.
+
+The built-in tool that is available to you
+is the `diffsettings` command.
+This tool makes it easy
+to see the computed settings
+of your module.
+Since settings can come
+from multiple files
+(including Django's `global_settings.py`)
+or environment variables,
+inspecting the settings output
+of `diffsettings` is more convenient
+than thinking through how a setting is set.
+
+By default,
+`diffsettings` will show a comparison
+of the settings module
+to the default Django settings.
+Settings that aren't in the defaults are marked
+with `###` after the value
+to indicate that they are different.
+
+I find that the default output is not the most useful mode.
+Instead,
+you can instruct `diffsettings`
+to output in a "unified" format.
+This format looks a lot more like a code diff.
+In addition,
+Django will colorize that output
+so that it's easier to see.
+Here's an example
+of some of the security settings
+by running `./manage.py diffsettings --output unified`
+for one of my projects.
+
+```diff
+- SECURE_HSTS_INCLUDE_SUBDOMAINS = False
++ SECURE_HSTS_INCLUDE_SUBDOMAINS = True
+- SECURE_PROXY_SSL_HEADER = None
++ SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
+```
+
+Finally,
+I'll note that you can actually compare two separate settings modules.
+Let's say you wanted to compare settings
+between your development mode
+and your live site.
+Assuming your settings files have names
+like I described earlier,
+you could run something like:
+
+```bash
+$ ./manage.py diffsettings \
+ --default project.settings.dev \
+ --settings project.settings.production \
+ --output unified
+```
+
+By using the `--default` flag,
+we instruct Django that `project.settings.dev` is the baseline
+for comparison.
+This version of the command will show where the two settings modules are different.
+
+Django only includes this single tool for working
+with settings,
+but I hope you can see that it's really handy.
+Now let's talk about a useful third party library
+that can help you with settings.
+
+{{< web >}}
+Earlier in the article,
+{{< /web >}}
+{{< book >}}
+Earlier in the chapter,
+{{< /book >}}
+I noted that dealing with environment variables has the pitfall
+of working with string data for everything.
+Thankfully, there is a package
+that can help you work
+with environment variables.
+The project is called {{< extlink "https://django-environ.readthedocs.io/en/latest/" "django-environ" >}}.
+django-environ primarily does two important things
+that I value:
+
+* The package allows you to coerce strings into a desired data type.
+* The package will read from a file
+ to load environment variables into your environment.
+
+What does type coercion look like?
+With `django-environ`,
+you start with an `Env` object.
+
+```python
+# project/settings.py
+
+import environ
+
+env = environ.Env()
+```
+
+The keyword arguments to `Env` describe the different environment variables
+that you expect the app to process.
+The key is the name of the environment variable.
+The value is a two element tuple.
+The first tuple element is the type you want,
+and the second element is a default value
+if the environment variable doesn't exist.
+
+If you want to be able to control `DEBUG`
+from an environment variable,
+the settings would be:
+
+```python
+# project/settings.py
+
+import environ
+
+env = environ.Env(
+ DEBUG=(bool, False),
+)
+
+DEBUG = env("DEBUG")
+```
+
+With this setup,
+your app will be safe by default
+with `DEBUG` set to `False`,
+but you'll be able to override
+that via the environment.
+`django-environ` works with a handful
+of strings that it will accept as `True`
+such as "on", "yes", "true", and others
+(see the documentation for more details).
+
+Once you start using environment variables,
+you'll want a convenient way to set them
+when your app runs.
+Manually calling `export` for all your variables
+before running your app is a totally unsustainable way
+to run apps.
+
+The `Env` class comes with a handy class method named `read_env`.
+With this method,
+your app can read environment variables
+into `os.environ`
+from a file.
+Conventionally,
+this file is named `.env`,
+and the file contains a list of key/value pairs
+that you want as environment variables.
+Following our earlier example,
+here's how we could set our app
+to be in debug mode:
+
+```env
+# .env
+DEBUG=on
+```
+
+Back in the settings file, you'd include `read_env`:
+
+```python
+# project/settings.py
+
+import environ
+
+environ.Env.read_env()
+env = environ.Env(
+ DEBUG=(bool, False),
+)
+
+DEBUG = env("DEBUG")
+```
+
+If you use a `.env` file,
+you will occasionally find a need to put secrets
+into this file
+for testing.
+Since the file can be a source for secrets,
+you should add this to `.gitignore`
+or ignore it
+in whatever version control system you use.
+As time goes on,
+the list of variables and settings will likely grow,
+so it's also a common pattern
+to create a `.env.example` file
+that you can use as a template
+in case you ever need to start
+with a fresh clone of your repository.
+
+## My Preferred Settings Setup
+
+Now we've looked at multiple strategies
+and tools for managing settings.
+I've used many of these schemes
+on various Django projects,
+so what is my preferred setup?
+
+For the majority of use cases,
+I find that working with `django-environ`
+in a single file is the best pattern
+in my experience.
+
+When I use this approach,
+I make sure that all of my settings favor a safe default configuration.
+This minimizes the configuration
+that I have to do for a live site.
+
+I like the flexibility
+of the pattern,
+and I find that I can quickly set certain configurations
+when developing.
+For instance,
+when I want to do certain kinds of testing
+like checking email rendering,
+I'll call something like:
+
+```bash
+$ EMAIL_TESTING=on ./manage.py runserver
+```
+
+My settings file has a small amount of configuration
+to alter the email settings
+to point emails
+to a local SMTP server tool
+called {{< extlink "https://github.com/mailhog/MailHog" "MailHog" >}}.
+Because I set an environment variable directly
+on my command line call,
+I can easily switch
+into a mode that sends email
+to MailHog
+for quick review.
+
+Overall,
+I like the environment variable approach,
+but I do use more than one settings file
+for one important scenario: testing.
+
+When I run my unit tests,
+I want to guarantee
+that certain conditions are always true.
+There are things
+that a test suite should never do
+in the vast majority of cases.
+Sending real emails is a good example.
+If I happen to configure my `.env`
+to test real emails for the local environment,
+I don't want my tests
+to send out an email accidentally.
+
+Thus,
+I create a separate testing settings file
+and configure my test runner (pytest)
+to use those settings.
+This settings file *does* mostly use the base environment,
+but I'll override some settings with explicit values.
+Here's how I protect myself
+from accidental live emails:
+
+```python
+# project/testing_settings.py
+
+from .settings import *
+
+# Make sure that tests are never sending real emails.
+EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend"
+```
+
+Even though my `Env` will look for an `EMAIL_BACKEND` environment variable
+to configure that setting dynamically,
+the testing setting is hardcoded
+to make email sending accidents impossible.
+
+The combination of a single file
+for most settings supplemented
+with a testing settings file
+for safety
+is the approach
+that has worked the best for me.
+
+## Summary
+
+{{< web >}}
+In this article,
+{{< /web >}}
+{{< book >}}
+In this chapter,
+{{< /book >}}
+you learned about Django settings
+and how to manage the configuration
+of your application.
+We covered:
+
+* How Django is configured
+* Patterns for working with settings in your projects
+* Tools that help you observe and manage settings
+
+{{< web >}}
+In the next article,
+{{< /web >}}
+{{< book >}}
+In the next chapter,
+{{< /book >}}
+we will look at how to handle files and media
+provided by users
+(e.g., profile pictures).
+You'll learn about:
+
+* How Django models maintain references to files
+* How the files are managed in Django
+* Packages that can store files in various cloud services
+
+{{< web >}}
+If you'd like to follow along
+with the series,
+please feel free to sign up
+for my newsletter
+where I announce all of my new content.
+If you have other questions,
+you can reach me online
+on Twitter
+where I am
+{{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
+{{< /web >}}
+
diff --git a/content/understand-django/2021-09-14-media-files.pt.md b/content/understand-django/2021-09-14-media-files.pt.md
new file mode 100644
index 00000000..3480ec11
--- /dev/null
+++ b/content/understand-django/2021-09-14-media-files.pt.md
@@ -0,0 +1,543 @@
+---
+title: "User File Use"
+description: >-
+ Maybe your app needs to handle files
+ from users
+ like profile pictures.
+ Accepting files from others is tricky
+ to do safely.
+ In this article,
+ we'll see the tools
+ that Django provides
+ to manage files safely.
+image: img/django.png
+type: post
+categories:
+ - Python
+ - Django
+tags:
+ - Python
+ - Django
+ - files
+series: "Understand Django"
+
+---
+
+In the last
+{{< web >}}
+[Understand Django]({{< ref "/understand-django/_index.md" >}})
+article,
+{{< /web >}}
+{{< book >}}
+chapter,
+{{< /book >}}
+you learned about Django settings
+and how to manage the configuration
+of your application.
+We also looked at tools
+to help you
+define settings
+effectively.
+
+{{< web >}}
+With this article,
+{{< /web >}}
+{{< book >}}
+With this chapter,
+{{< /book >}}
+we're going to dig into file management.
+Unlike the static files
+that you create for the app yourself,
+you may want your app to accept files
+from your users.
+Profile pictures are a good example
+of user files.
+You'll see how Django handles those kinds
+of files
+and how to deal with them safely.
+
+{{< understand-django-series "files" >}}
+
+## Files In Django Models
+
+{{< web >}}
+As we saw in the models article,
+{{< /web >}}
+{{< book >}}
+As we saw in the models chapter,
+{{< /book >}}
+model fields in a Django model map
+to a column in a database table.
+When you want to access the *data*
+for a model instance,
+Django will pull the data
+from a database row.
+
+Dealing with files in models is a bit different.
+While it *is* possible to store file data directly
+in a database,
+you won't see that happen often.
+The reason is that storing the data in the database
+usually affects the performance
+of the database,
+especially with a large number of files.
+
+Instead,
+a common pattern
+in database usage
+is to store files separately
+from the database itself.
+Within the database,
+a column would store some kind of *reference*
+to the stored file
+like a path
+if files are stored on a filesystem.
+This is the approach
+that Django takes
+with files.
+
+Now that you know that Django takes this approach,
+you can remember:
+
+1. Django models hold the *reference* to a file (e.g., a file path)
+2. The file *data* (i.e., the file itself) is stored somewhere else.
+
+The "somewhere else" is called the "file storage,"
+and we'll discuss storage
+in more depth
+in the next section.
+
+Let's focus on the first item.
+What do you use to reference the files?
+Like all other model data,
+we'll use a field!
+Django includes two fields
+that help with file management:
+
+* `FileField`
+* `ImageField`
+
+### `FileField`
+
+What if you want to store a profile picture?
+You might do something like this:
+
+```python
+# application/models.py
+
+from django.db import models
+
+class Profile(models.Model):
+ picture = models.FileField()
+ # Other fields like a OneToOneKey to User ...
+```
+
+This is the most basic version of using file fields.
+We can use this model very directly
+with a Django shell
+to illustrate file management.
+
+```python
+$ ./manage.py shell
+>>> from django.core.files import File
+>>> from application.models import Profile
+>>> f = open('/Users/matt/path/to/image.png')
+>>> profile = Profile()
+>>> profile.picture.save(
+... 'my-image.png',
+... File(f)
+... )
+```
+
+In this example,
+I'm creating a profile instance manually.
+There are a few interesting notes:
+
+* The `File` class is an important wrapper
+ that Django uses
+ to make Python file objects (i.e., the value returned from `open`) work
+ with the storage system.
+* The name `image.png` and `my-image.png` do not have to match.
+ Django can store the content of `image.png`
+ and use `my-image.png`
+ as the name to reference
+ within the storage system.
+* Saving the picture will automatically save the parent model instance
+ by default.
+
+More often than not,
+you won't need to use these interfaces directly
+because Django has form fields
+and other tools
+that manage much of this for you.
+
+The current model example raises questions.
+
+* Where does that data go?
+* What if we have a name conflict between two files like "`my-image.png`"?
+* What happens if we try to save something that isn't an image?
+
+If we make no changes to the current setup,
+the data will go into the root
+of the media file storage.
+Media file storage is a topic that will be covered later.
+For the moment,
+recognize that putting all the files into a single place (i.e., the root)
+will be a mess.
+This mess will be pronounced if you're trying to track many file fields,
+but we can fix this with the `upload_to` field keyword argument.
+The simplest version of `upload_to` can take a string
+that the storage logic will use as a directory prefix
+to scope content
+into a different area.
+
+We're still left with potentially conflicting filenames.
+Thankfully,
+`upload_to` can also accept a callable
+that gives us a chance to fix that issue.
+Let's rework the example.
+
+```python
+# application/models.py
+
+import uuid
+from pathlib import Path
+from django.db import models
+
+def profile_pic_path(
+ instance,
+ filename
+ ):
+ path = Path(filename)
+ return "profile_pics/{}{}".format(
+ uuid.uuid4(),
+ path.suffix
+ )
+
+class Profile(models.Model):
+ picture = models.FileField(
+ upload_to=profile_pic_path
+ )
+ # Other fields like a OneToOneKey to User ...
+```
+
+With this new version
+of the profile model,
+all of the images will be stored
+in a `profile_pics` path
+within the file storage.
+
+This version also solves the duplicate filename problem.
+`profile_pic_path` ignores most
+of the original filename provided.
+If two users both happen to upload `profile-pic.jpg`,
+`profile_pic_path` will assign those images random IDs
+and ignore the `profile-pic` part
+of the filename.
+
+You can see that the function calls `uuid4()`.
+These are effectively random IDs called
+{{< extlink "https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_(random)" "Universally Unique Identifiers (UUID)" >}}.
+UUIDs are likely something that you've seen before
+if you've worked with computers long enough,
+even if you didn't know their name.
+An example UUID would be `76ee4ae4-8659-4b50-a04f-e222df9a656a`.
+In the storage area,
+you might find a file stored as:
+
+```text
+profile_pics/76ee4ae4-8659-4b50-a04f-e222df9a656a.jpg
+```
+
+Each call to `uuid4()` is nearly certain
+to generate a unique value.
+Because of this feature,
+we can avoid filename conflicts
+by storing profile pictures
+with a unique name.
+As an aside,
+UUIDs are not very friendly for users,
+so if you plan to let your users download these files,
+you might wish to explore alternative naming techniques.
+
+There's one more problem to fix
+in this example.
+How do we know that a user provided a valid image file?
+This is important to check,
+because we want to avoid storing malicious files
+that bad actors might upload
+to our apps.
+
+This is where the `ImageField` has value.
+This field type contains extra validation logic
+that can check the *content* of the file
+to check that the file is, in fact, an image.
+To use `ImageField`,
+you'll need to install the
+{{< extlink "https://pillow.readthedocs.io/en/latest/" "Pillow" >}} library.
+Pillow is a package
+that lets Python work with image data.
+
+Our final example looks like:
+
+```python
+# application/models.py
+
+import uuid
+from pathlib import Path
+from django.db import models
+
+def profile_pic_path(
+ instance,
+ filename
+ ):
+ path = Path(filename)
+ return "profile_pics/{}{}".format(
+ uuid.uuid4(),
+ path.suffix
+ )
+
+class Profile(models.Model):
+ picture = models.ImageField(
+ upload_to=profile_pic_path
+ )
+ # Other fields like a OneToOneKey to User ...
+```
+
+Now that we've seen how Django will track files and images
+in your models,
+let's go deeper
+and try to understand the file storage features.
+
+## Files Under The Hood
+
+We now know that models store references to files
+and not the files themselves.
+The file storage task is delegated
+to a special Python class
+in the system.
+
+This Python class must implement
+{{< extlink "https://docs.djangoproject.com/en/4.1/ref/files/storage/" "a specific API" >}}.
+Why?
+Like so many other parts of Django,
+the storage class can be swapped out
+for a different class.
+We've seen this swappable pattern already
+with templates, databases, authentication, static files, and sessions.
+
+The setting to control which type
+of file storage Django uses is `DEFAULT_FILE_STORAGE`.
+This setting is a Python module path string
+to the specific class.
+
+So, what's the default?
+The default is a storage class
+that will store files locally
+on the server
+that runs the app.
+This is found at `django.core.files.storage.FileSystemStorage`.
+The storage class uses a couple
+of important settings:
+`MEDIA_ROOT` and `MEDIA_URL`.
+
+The `MEDIA_ROOT` setting defines
+where Django should look for files in the filesystem.
+
+```python
+MEDIA_ROOT = BASE_DIR / "media"
+```
+
+On my computer,
+with the above setting
+and the `Profile` class example
+from earlier,
+Django would store a file somewhere like:
+
+```text
+# This path is split to be easier to read.
+/Users/matt/example-app/ \
+ media/profile_pics/ \
+ 76ee4ae4-8659-4b50-a04f-e222df9a656a.jpg
+```
+
+The other setting important to `FileSystemStorage` is `MEDIA_URL`.
+This setting will determine how files are accessed
+by browsers
+when Django is running.
+Let's say `MEDIA_URL` is:
+
+```python
+MEDIA_URL = "/media/"
+```
+
+Our profile picture would have a URL like:
+
+```python
+>>> from application.models import Profile
+>>> profile = Profile.objects.last()
+>>> profile.picture.url
+'/media/profile_pics/76ee4ae4-8659-4b50-a04f-e222df9a656a.jpg'
+```
+
+This is the path that we can reference
+in templates.
+An image tag template fragment would look like:
+
+{{< web >}}
+```django
+
+```
+{{< /web >}}
+{{< book >}}
+```djangotemplate
+
+```
+{{< /book >}}
+
+The Django documentation shows how file storage is a specific interface.
+`FileSystemStorage` happens to be included
+with Django and implements this interface
+for the simplest storage mechanism,
+the file system
+of your server's operating system.
+
+We can also store files separately
+from the web server,
+and there are often really good reasons to do that.
+Up next,
+we'll look at another option
+for file storage aside from the provided default.
+
+## Recommended Package
+
+What is a problem
+that can arise
+if you use the built-in `FileSystemStorage`
+to store files
+for your application?
+There are actually many possible problems!
+Here are a few:
+
+* The web server can have too many files and run out of disk space.
+* Users may upload malicious files
+ to attempt to gain control
+ of your server.
+* Users can upload large files
+ that can cause a Denial of Service (DOS) attack
+ and make your site inaccessible.
+
+If you conclude that `FileSystemStorage` will not work
+for your app,
+is there another good option?
+Absolutely!
+
+The most popular storage package
+to reach for is
+{{< extlink "https://django-storages.readthedocs.io/en/latest/" "django-storages" >}}.
+django-storages includes a set of storage classes
+that can connect
+to a variety
+of cloud services.
+These cloud services are able to store an arbitrary number of files.
+With django-storages,
+your application can connect to services like:
+
+* Amazon Simple Storage Service (S3)
+* Google Cloud Storage
+* Digital Ocean Spaces
+* Services you run separately like an SFTP server
+
+These services would have additional cost
+beyond the cost of running your web server
+in the cloud,
+but the services usually have shockingly low rates
+and some offer a generous free tier
+for lower levels of data storage.
+
+Why use django-storages?
+
+* You will never need to worry about disk space.
+ The cloud services offer effectively unlimited storage space
+ if you're willing to pay for it.
+* The files will be separated from your Django web server.
+ This can eliminate some categories of security problems
+ like a malicious file trying to execute arbitrary code
+ on the web server.
+* Cloud storage can offer some caching benefits
+ and be easily connected to Content Delivery Networks
+ to optimize how files are served to your app's users.
+
+As with all software choices,
+we have tradeoffs to consider
+when using different storage classes.
+On its face,
+django-storages seems to be nearly all positives.
+The benefits come with some setup complexity cost.
+
+For instance,
+I like to use Amazon S3
+for file storage.
+You can see from the
+{{< extlink "https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html" "Amazon S3 setup" >}} documentation
+that there is a fair amount of work to do
+beyond setting a different `DEFAULT_FILE_STORAGE` class.
+This setup includes setting AWS private keys,
+access controls,
+regions,
+buckets,
+and a handful of other important settings.
+
+While the setup cost exists,
+you'll usually pay that cost at the beginning
+of a project
+and be mostly hands off after that.
+
+django-storages is a pretty fantastic package,
+so if your project has a lot of files to manage,
+you should definitely consider using it
+as an alternative to the `FileSystemStorage`.
+
+## Summary
+
+{{< web >}}
+In this article,
+{{< /web >}}
+{{< book >}}
+In this chapter,
+{{< /book >}}
+you learned about Django file management.
+We covered:
+
+* How Django models maintain references to files
+* How the files are managed in Django
+* A Python package that can store files in various cloud services
+
+{{< web >}}
+In the next article,
+{{< /web >}}
+{{< book >}}
+In the next chapter,
+{{< /book >}}
+let's explore commands.
+Commands are the code
+that you can run with `./manage.py`.
+You'll learn about:
+
+* Built-in commands provided by Django
+* How to build custom commands
+* Extra commands from the community that are useful extensions for apps
+
+{{< web >}}
+If you'd like to follow along
+with the series,
+please feel free to sign up
+for my newsletter
+where I announce all of my new content.
+If you have other questions,
+you can reach me online
+on Twitter
+where I am
+{{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
+{{< /web >}}
+
diff --git a/content/understand-django/2021-11-04-command-apps.pt.md b/content/understand-django/2021-11-04-command-apps.pt.md
new file mode 100644
index 00000000..b48b0444
--- /dev/null
+++ b/content/understand-django/2021-11-04-command-apps.pt.md
@@ -0,0 +1,708 @@
+---
+title: "Command Your App"
+description: >-
+ With this Understand Django article,
+ you'll learn about commands.
+ Commands are the way to execute scripts
+ that interact with your Django app.
+ We'll see built-in commands
+ and how to build your own commands.
+image: img/django.png
+type: post
+categories:
+ - Python
+ - Django
+tags:
+ - Python
+ - Django
+ - commands
+series: "Understand Django"
+
+---
+
+In the last
+{{< web >}}
+[Understand Django]({{< ref "/understand-django/_index.md" >}})
+article,
+{{< /web >}}
+{{< book >}}
+chapter,
+{{< /book >}}
+we dug into file management.
+We saw how Django handles user uploaded files
+and how to deal with them safely.
+
+{{< web >}}
+With this article,
+{{< /web >}}
+{{< book >}}
+With this chapter,
+{{< /book >}}
+you'll learn about commands.
+Commands are the way to execute scripts
+that interact with your Django app.
+We'll see built-in commands
+and how to build your own.
+
+{{< understand-django-series "commands" >}}
+
+## Why Commands?
+
+Django makes it possible
+to run code from a terminal
+with `./manage.py`,
+but why is this helpful or needed?
+Consider this script:
+
+```python
+# product_stat.py
+
+from application.models import Product
+
+print(Product.objects.count())
+```
+
+which you could try running with:
+
+```bash
+$ python product_stat.py
+```
+
+The problem with this script is that Django is not ready to run yet.
+If you tried to run this kind of code,
+you would get an `ImproperlyConfigured` exception.
+There are a couple of modifications
+that you could make to get the script to run.
+
+* Call `django.setup()`.
+* Specify the `DJANGO_SETTINGS_MODULE`.
+
+```python
+# product_stat.py
+import django
+
+django.setup()
+
+from application.models import Product
+
+print(Product.objects.count())
+```
+
+Note that `django.setup()` must be before your Django related imports
+(like the `Product` model in this example).
+Now the script can run if you supply where the settings are located too.
+
+```bash
+$ DJANGO_SETTING_MODULE=project.settings python product_stat.py
+```
+
+This arrangement is less than ideal,
+but why else might we want a way to run commands through Django?
+
+Try running `./manage.py -h`.
+
+What you'll likely see is more commands
+than what Django provides alone.
+This is where we begin to see more value
+from the command system.
+Because Django provides a standard way
+to run scripts,
+other Django applications can bundle useful commands
+and make them easily accessible
+to you.
+
+Now that you've had a chance to see why commands exist
+to run scripts
+for Django apps,
+let's back up and see *what* commands are.
+
+## We Hereby Command
+
+Django gives us a tool to run commands
+before we've even started our project.
+That tool is the `django-admin` script.
+We saw it all the way back
+{{< web >}}
+in the first article
+{{< /web >}}
+{{< book >}}
+in the first chapter
+{{< /book >}}
+where I provided a short set
+of setup instructions
+to get you started
+if you've never used Django before.
+
+After you've started a project,
+your code will have a `manage.py` file,
+{{< web >}}
+and the commands you've seen in most articles are in the form of:
+{{< /web >}}
+{{< book >}}
+and the commands you've seen in most chapters are in the form of:
+{{< /book >}}
+
+```bash
+$ ./manage.py some_command
+```
+
+What's the difference
+between `django-admin` and `manage.py`?
+In truth, **not much!**
+
+`django-admin` comes from Django's Python packaging.
+In Python packages,
+package developers can create scripts
+by defining an entry point
+in the {{< extlink "https://github.com/django/django/blob/4.1/setup.cfg" "packaging configuration" >}}.
+In Django,
+this configuration looks like:
+
+```ini
+[options.entry_points]
+console_scripts =
+ django-admin = django.core.management:execute_from_command_line
+```
+
+Meanwhile,
+the entire `manage.py`
+of a Django project looks like:
+
+```python
+#!/usr/bin/env python
+"""Django's command-line utility for administrative tasks."""
+import os
+import sys
+
+
+def main():
+ os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project.settings')
+ try:
+ from django.core.management import execute_from_command_line
+ except ImportError as exc:
+ raise ImportError(
+ "Couldn't import Django. Are you sure it's installed and "
+ "available on your PYTHONPATH environment variable? Did you "
+ "forget to activate a virtual environment?"
+ ) from exc
+ execute_from_command_line(sys.argv)
+
+
+if __name__ == '__main__':
+ main()
+```
+
+If you look closely,
+you can see that the different scripts are both ways
+to invoke the `execute_from_command_line` function
+to run a command.
+The primary difference is that the latter script will attempt
+to set the `DJANGO_SETTINGS_MODULE` environment variable automatically.
+Since Django needs to have `DJANGO_SETTINGS_MODULE` defined
+for most commands
+(note: `startproject` does *not* require that variable),
+`manage.py` is a more convenient way
+to run commands.
+
+`execute_from_command_line` is able to present
+what commands are available to a project,
+whether a command comes from Django itself,
+an installed app,
+or is a custom command that you created yourself.
+How are the commands discovered?
+*The command system does discovery by following some packaging conventions.*
+
+Let's say your project has an app named `application`.
+Django can find the command if you have the following packaging structure.
+
+```text
+application
+├── __init__.py
+├── management
+│ ├── __init__.py
+│ └── commands
+│ ├── __init__.py
+│ └── custom_command.py
+├── models.py
+└── views.py
+... Other typical Django app files
+```
+
+With this structure, you could run:
+
+```bash
+$ ./manage.py custom_command
+```
+
+Notes:
+
+* Django will create a command for a module found
+ in `/management/commands/.py`.
+* Don't forget the `__init__.py` files!
+ Django can only discover the commands
+ if `management` and `commands` are proper Python package directories.
+* The example uses `custom_command`,
+ but you can name your command
+ with whatever valid Python module name that you want.
+
+Unfortunately,
+we can't slap some Python code
+into `custom_command.py`
+and assume that Django will know how to run it.
+Within the `custom_command.py` module,
+Django needs to find a `Command` class
+that subclasses a `BaseCommand` class
+that is provided by the framework.
+Django requires this structure to give command authors a consistent way
+to access features
+of the command system.
+
+With the `Command` class,
+you can add a `help` class attribute.
+Adding help can give users a description
+of what your command does when running
+`./manage.py custom_command -h`.
+
+The `Command` class will also help you with handling arguments.
+If your command needs to work with user input,
+you'll need to parse that data.
+Thankfully,
+the class integrates
+with Python's built-in `argparse` module.
+By including an `add_arguments` method,
+a command can parse the data
+and pass the results
+to the command's handler method in a structured way.
+If you've had to write Python scripts before,
+then you may understand how much time this kind of parsing can save you
+(and for those who haven't, the answer is "a lot of time!").
+
+Other smaller features exist within the `Command` class too.
+Perhaps you only want your command to run
+if your project has satisfied certain pre-conditions.
+Commands can use the `requires_migration_checks`
+or `requires_system_checks`
+to ensure that the system is in the correct state
+before running.
+
+I hope it's clear
+that the goal of the `Command` class is
+to help you with common actions
+that many commands will need to use.
+There is a small API to learn,
+but the system is a boon to making scripts quickly.
+
+## Command By Example
+
+Let's consider a powerful use case
+to see a command in action.
+When you initially start a Django app,
+all of your app's interaction will probably be through web pages.
+After all,
+you were trying to use Django to make a web app,
+right?
+What do you do when you need to do something
+that doesn't involve a browser?
+
+This kind of work for your app is often considered *background* work.
+Background work is a pretty deep topic
+and will often involve special background task software
+like
+{{< extlink "https://docs.celeryproject.org/en/stable/getting-started/introduction.html" "Celery" >}}.
+When your app is at an early stage,
+Celery or similar software can be overkill and far more than you need.
+
+A simpler alternative for some background tasks could be a command paired
+with a scheduling tool like
+{{< extlink "https://en.wikipedia.org/wiki/Cron" "cron" >}}.
+
+On one of my projects,
+I offer free trials for accounts.
+After 60 days,
+the free trial ends
+and users either need to pay
+for the service
+or discontinue using it.
+By using a command
+and pairing it
+with the
+{{< extlink "https://devcenter.heroku.com/articles/scheduler" "Heroku Scheduler" >}},
+I can move accounts from their trial status
+to expired
+with a daily check.
+
+The following code is very close
+to what my `expire_trials` command looks like
+in my app.
+I've simplified things a bit,
+so that you can ignore the details
+that are specific to my service.
+
+```python
+# application/management/commands/expire_trials.py
+
+import datetime
+
+from django.core.management.base import BaseCommand
+from django.utils import timezone
+
+from application.models import Account
+
+
+class Command(BaseCommand):
+ help = "Expire any accounts that are TRIALING beyond the trial days limit"
+
+ def handle(self, *args, **options):
+ self.stdout.write(
+ "Search for old trial accounts..."
+ )
+ # Give an extra day to be gracious and avoid customer complaints.
+ cutoff_days = 61
+ trial_cutoff = timezone.now() - datetime.timedelta(days=cutoff_days)
+ expired_trials = Account.objects.filter(
+ status=Account.TRIALING, created__lt=trial_cutoff
+ )
+ count = expired_trials.update(
+ status=Account.TRIAL_EXPIRED
+ )
+ self.stdout.write(
+ f"Expired {count} trial(s)"
+ )
+```
+
+I configured the scheduler
+to run `python manage.py expire_trials`
+every day
+in the early morning.
+The command checks the current time
+and looks for `Account` records
+in the trialing state
+that were created before the cutoff time.
+From that query set,
+the affected accounts are set to the expired account state.
+
+How can you test this command?
+There are a couple of approaches you can take
+when testing a command.
+
+If you need to simulate calling the command
+with command line arguments,
+then you can use `call_command`
+from `django.core.management`.
+Since the example command doesn't require arguments,
+I didn't take that approach.
+
+Generally, my preference is to create a command object
+and invoke the `handle` method directly.
+In my example above,
+you can see that the command uses `self.stdout`
+instead of calling `print`.
+Django does this so that you could check your output
+if desired.
+
+Here is a test for this command:
+
+```python
+# application/tests/test_commands.py
+
+from io import StringIO
+
+from application.management.commands.expire_trials import (
+ Command
+)
+from application.models import Account
+from application.tests.factories import AccountFactory
+
+def test_expires_trials():
+ """Old trials are marked as expired."""
+ stdout = StringIO()
+ account = AccountFactory(
+ status=Account.TRIALING,
+ created=timezone.now() - datetime.timedelta(days=65),
+ )
+ command = Command(stdout=stdout)
+
+ command.handle()
+
+ account.refresh_from_db()
+ assert account.status == Account.TRIAL_EXPIRED
+ assert "Expired 1 trial(s)" in stdout.getvalue()
+```
+
+In this test,
+I constructed a command instance
+and checked the account state
+after the command invocation.
+Also,
+observe that the `StringIO` instance is injected
+into the `Command` constructor.
+By building the command this way,
+checking the output becomes a very achievable task
+via the `getvalue` method.
+
+Overall,
+this scheme of making a command
+and running it on a schedule
+avoids all the work
+of setting up a background worker process.
+I've been extremely satisfied
+with how this technique has worked
+for me,
+and I think it's a great pattern
+when your app doesn't have to do a lot
+of complex background processing.
+
+## Useful Commands
+
+Django is full of
+{{< extlink "https://docs.djangoproject.com/en/4.1/ref/django-admin/" "useful commands" >}}
+that you can use for all kinds of purposes.
+{{< web >}}
+Thus far in this series,
+{{< /web >}}
+{{< book >}}
+Thus far in this book,
+{{< /book >}}
+we've discussed a bunch of them, including:
+
+* `check` - Checks that your project is in good shape.
+* `collectstatic` - Collects static files into a single directory.
+* `createsuperuser` - Creates a super user record.
+* `makemigrations` - Makes new migration files based on model changes.
+* `migrate` - Runs any unapplied migrations to your database.
+* `runserver` - Runs a development web server to check your app.
+* `shell` - Starts a Django shell that allows you to use Django code
+ on the command line.
+* `startapp` - Makes a new Django app from a template.
+* `startproject` - Makes a new Django project from a template.
+* `test` - Executes tests that check the validity of your app.
+
+Here is a sampling of other commands
+that I find useful
+when working with Django projects.
+
+### `dbshell`
+
+The `dbshell` command starts a different kind of shell.
+The shell is a database program
+that will connect to the same database
+that your Django app uses.
+This shell will vary based on your choice of database.
+
+For instance,
+when using PostgreSQL,
+`./manage.py dbshell`
+will start `{{< extlink "https://www.postgresql.org/docs/current/app-psql.html" "psql" >}}`.
+From this shell,
+you can execute SQL statements directly
+to inspect the state
+of your database.
+I don't reach for this command often,
+but I find it very useful to connect
+to my database without having to remember database credentials.
+
+### `showmigrations`
+
+The `showmigrations` command has a simple job.
+The command shows all the migrations
+for each Django app
+in your project.
+Next to each migration is an indicator
+of whether the migration is applied
+to your database.
+
+Here is an example of the `users` app
+from one of my Django projects:
+
+```bash
+$ ./manage.py showmigrations users
+users
+ [X] 0001_initial
+ [X] 0002_first_name_to_150_max
+ [ ] 0003_profile
+```
+
+In my real project,
+I've applied all the migrations,
+but for this example,
+I'm showing the third migration
+as it would appear
+if the migration wasn't applied yet.
+
+`showmigrations` is a good way to show the state
+of your database
+from Django's point of view.
+
+### `sqlmigrate`
+
+The `sqlmigrate` command is *very* handy.
+The command will show you what SQL statements Django would run
+for an individual migration file.
+
+Let's see an example.
+In Django 3.1,
+the team changed the `AbstractUser` model
+so that the `first_name` field could have a maximum length
+of 150 characters.
+Anyone using the `AbstractUser` model
+(which includes me)
+had to generate a migration
+to apply that change.
+
+From my `showmigrations` output above,
+you can see that the second migration
+of my `users` app applied this particular framework change.
+
+To see the Postgres SQL statements
+that made the change,
+I can run:
+
+```bash
+$ ./manage.py sqlmigrate users 0002
+BEGIN;
+--
+-- Alter field first_name on user
+--
+ALTER TABLE "users_user" ALTER COLUMN "first_name" TYPE varchar(150);
+COMMIT;
+```
+
+From this,
+we can tell that Postgres executed an `ALTER COLUMN`
+{{< extlink "https://en.wikipedia.org/wiki/Data_definition_language" "DDL" >}}
+statement
+to modify the length
+of the `first_name` field.
+
+### `squashmigrations`
+
+Django migrations are a stack of separate database changes
+that produce a final desired schema state
+in your database.
+Over time,
+your Django apps will accumulate migration files,
+but those files have a shelf life.
+The `squashmigrations` command is designed
+to let you tidy up an app's set
+of migration files.
+
+By running `squashmigrations`,
+you can condense an app's migrations
+into a significantly smaller number.
+The reduced migrations can accurately represent your database schema,
+and make it easier to reason about what changes happened
+in the app's history.
+As a side benefit,
+migration squashing can make Django's migration handling faster
+because Django gets to process fewer files.
+
+## Even More Useful Commands
+
+The commands above come with the standard Django install.
+Adding in third-party libraries gives you access
+to even more cool stuff to help with your project development!
+
+A package that I often reach for
+with my Django projects is the
+{{< extlink "https://django-extensions.readthedocs.io/en/latest/index.html" "django-extensions" >}}
+package.
+This package is full of goodies,
+including some great optional commands
+that you can use!
+
+A couple of my favorites include:
+
+### `shell_plus`
+
+How often do you fire up a Django shell,
+import a model,
+then do some ORM queries
+to see the current state of the database?
+This is something I do *quite* often.
+
+The `shell_plus` command is like the regular shell,
+but the command will import all your models *automatically*.
+For the five extra characters
+of `_plus`,
+you can save your fingers a lot of typing
+to import your models
+and get directly to whatever you needed the shell for.
+
+The command will also import some commonly used Django functions
+and features
+like `reverse`, `settings,` `timezone`, and more.
+
+Also,
+if you have installed a separate REPL like
+{{< extlink "https://ipython.org/" "IPython" >}},
+`shell_plus` will attempt to use the alternate REPL instead
+of the default version that comes with Python.
+
+### `graph_models`
+
+When I'm live streaming my side projects
+on {{< extlink "https://www.youtube.com/c/MattLayman" "my YouTube channel" >}},
+I will often want to show the model relationships
+of my Django project.
+With the `graph_models` command,
+I can create an image of all my models
+and how those models relate to each other
+(using UML syntax).
+This is a great way to:
+
+* Remind myself of the data modeling choices in my apps.
+* Orient others to what I'm doing with my project.
+
+This particular command requires some extra setup
+to install the right tools
+to create images,
+but the setup is manageable
+and the results are worth it.
+
+Aside from `shell_plus` and `graph_models`,
+there are 20 other commands that you can use
+that may be very useful to you.
+You should definitely check out django-extensions.
+
+## Summary
+
+{{< web >}}
+In this article,
+{{< /web >}}
+{{< book >}}
+In this chapter,
+{{< /book >}}
+you saw Django commands.
+We covered:
+
+* Why commands exist in the Django framework
+* How commands work
+* How to create your own custom command and how to test it
+* Useful commands from the core framework and the django-extensions package
+
+{{< web >}}
+In the next article,
+{{< /web >}}
+{{< book >}}
+In the next chapter,
+{{< /book >}}
+we're going to look into performance.
+You'll learn about:
+
+* How Django sites get slow
+* Ways to optimize your database queries
+* How to apply caching to save processing
+
+{{< web >}}
+If you'd like to follow along
+with the series,
+please feel free to sign up
+for my newsletter
+where I announce all of my new content.
+If you have other questions,
+you can reach me online
+on Twitter
+where I am
+{{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
+{{< /web >}}
+
diff --git a/content/understand-django/2022-01-19-go-fast.pt.md b/content/understand-django/2022-01-19-go-fast.pt.md
new file mode 100644
index 00000000..41c51e03
--- /dev/null
+++ b/content/understand-django/2022-01-19-go-fast.pt.md
@@ -0,0 +1,1163 @@
+---
+title: "Go Fast With Django"
+description: >-
+ How do you make your Django app fast?
+ You measure what is slow,
+ scale your system when necessary,
+ and use a combination
+ of fast database queries
+ and strategic caching.
+ In this Understand Django article,
+ we'll explore those topics and more
+ to help you get a performant Django app.
+image: img/django.png
+type: post
+categories:
+ - Python
+ - Django
+tags:
+ - Python
+ - Django
+ - performance
+ - caching
+series: "Understand Django"
+
+---
+
+In the last
+{{< web >}}
+[Understand Django]({{< ref "/understand-django/_index.md" >}})
+article,
+{{< /web >}}
+{{< book >}}
+chapter,
+{{< /book >}}
+we learned about commands.
+Commands are the way to execute scripts
+that interact with your Django app.
+
+{{< web >}}
+With this article,
+{{< /web >}}
+{{< book >}}
+With this chapter,
+{{< /book >}}
+we're going to dig into performance.
+How do you make your Django site faster?
+Keep reading to find out.
+
+{{< understand-django-series "performance" >}}
+
+## Theory Of Performance
+
+{{< web >}}
+>There are two ways to make a website faster:
+>
+> * Do **more** work.
+> * Do **less** work.
+{{< /web >}}
+{{< book >}}
+There are two ways to make a website faster:
+
+ * Do **more** work.
+ * Do **less** work.
+{{< /book >}}
+
+How we do more work *and* less work depends
+on the type of work
+that we're trying to address on the site.
+
+When we're talking about doing more work,
+what I'm really saying is that we often need
+to increase the ***throughput***
+of a site.
+Throughput is a measure of work over time.
+By increasing throughput,
+a site can serve more users concurrently.
+
+A very natural throughput measure
+on a Django site is *requests per second*.
+A page view on a site could contain multiple requests,
+so requests per second isn't a perfect analog
+to how many users your site can handle,
+but it's still a useful measure
+to help you reason about site performance.
+
+On the flip side of doing more work,
+what does it mean to do less work
+to improve performance?
+
+> The fastest code is no code.
+
+Every line of code that you write must be processed
+by a computer when it runs.
+If your code is inefficient
+or if too much is running,
+that will naturally mean
+that it will take longer to produce a final result.
+
+The time from when an input happens
+to when an output is received is called ***latency***.
+If a user clicks on a link
+on your site,
+how long does it take for them
+to get a response?
+That time delay is latency.
+
+Less work doesn't just mean
+that you're running too much code!
+There are a lot of factors
+that might contribute
+to latency,
+some of them are easier to optimize than others.
+
+* Inefficient code - mistakes in development can make a computer slower than necessary
+* Data size - sending more data requires more effort to deliver to users
+* Geographic location - the speed of light is a real limit on network communication
+* and more!
+
+If you can reduce latency
+on your site,
+you can improve the experience
+of the people using the site.
+
+{{< web >}}
+In the rest of this article,
+{{< /web >}}
+{{< book >}}
+In the rest of this chapter,
+{{< /book >}}
+you'll learn how you can do both more and less work
+to make a better site
+that will benefit your users.
+
+## Measure First
+
+Before optimizing,
+we have to recognize what kind of work
+is impacting an app.
+In other words,
+*what is the resource constraint
+that is preventing an app
+from performing better?*
+
+Measuring applications,
+especially those that are running
+with live traffic,
+can be a tricky endeavor.
+I think we can look at an app
+from a zoomed out macro level
+or a zoomed in point of view.
+
+I would start my analysis
+from inspecting the system overall
+for patterns.
+Broadly,
+you will find that performance tends
+to fall into a couple of major bottleneck categories:
+
+* I/O bound
+* CPU bound
+
+*I/O bound* means that the system is limited
+(that's the "bound" part)
+by the inputs and outputs
+of the system.
+Since that's still a really vague statement,
+let's make it more concrete.
+An I/O bound system is one
+that is waiting
+for work to be available.
+Classic examples include:
+
+* waiting for responses from a database
+* waiting for content from a file system
+* waiting for data to transfer over a network
+* and so on
+
+Optimizing an I/O bound system is all about minimizing those waiting moments.
+
+Conversely,
+*CPU bound* systems are systems
+that are drowning in immediate work
+to calculate.
+The computer's **C**entral **P**rocessing **U**nit can't keep up
+with all that it's being asked to do.
+Classic examples of CPU bound work are:
+
+* Data science computations for machine learning
+* Image processing and rendering
+* Test suite execution
+
+Optimizing a CPU bound system focuses heavily
+on making calculations faster and cheaper.
+
+Since we understand
+that an application
+that is underperforming
+is likely to be I/O bound or CPU bound,
+we can start to look
+for these patterns
+within the system.
+The easiest initial signal to observe is the CPU load.
+If the processors
+on your production machines
+are running at very high CPU utilization,
+then it's a pretty clear indicator
+that your app is CPU bound.
+In my estimation,
+you'll rarely see this
+for web applications.
+**Most underperforming web applications are likely to be I/O bound.**
+
+The reason that web apps are often I/O bound has to do
+with their typical function.
+Most apps are fetching data from a database
+and displaying it to a user.
+There isn't a massive amount of computation
+(comparatively to something like machine learning)
+that the app needs to do.
+Thus,
+your app is probably waiting around
+for data
+from the database.
+
+If you observe that the system is not CPU bound,
+then the next action is to dig deeper
+and find where the application is spending its time waiting.
+But how can we do this?
+Philosophically,
+you should now have an ok understanding
+of what to be looking for,
+but what tools can you use to accomplish the task
+of measurement?
+
+We need to rewind a bit.
+A moment ago,
+I also made an assumption
+that you know how to find the CPU load
+of your application.
+That may not be the case.
+Let's look at some tools
+that help you categorize
+where your app's resource bottleneck is.
+
+The easiest way to monitor basic resource information
+about your app
+including CPU, memory, disk usage, and more
+may come from your hosting vendor.
+My preferred hosting vendor, Heroku, displays all these kinds
+of metrics
+on a single page
+so I can assess system performance at a glance.
+Other vendors like Digital Ocean or AWS provide tools
+to help you see this information too.
+
+If your hosting vendor doesn't provide these tools,
+then you'll have to use other techniques.
+Presumably,
+if you're using a Virtual Private Server (VPS)
+for hosting,
+you have access to the server itself via ssh.
+On the server that's running your application,
+you can use a program like `top`.
+This is a classic program
+for checking which processes are using the "top" amount
+of resources.
+`top` will show the list of processes
+ordered by what is consuming the most CPU
+and will refresh the order of the list every second
+to provide a current snapshot in time.
+(**Tip**: use `q` to quit `top` after you start it.)
+
+While `top` is useful and gets the job done to learn about CPU usage,
+it's not exactly the friendliest tool out there.
+There are alternatives to `top`
+that may offer a better user experience.
+I personally find `top` sufficient,
+but I know `htop` is a popular alternative.
+
+If you don't have tools
+from your hosting provider
+and don't want to use ssh to log into a server,
+there are other options to consider.
+Broadly,
+this other category of tools is called Application Performance Monitoring (APM).
+APM tools are vendors
+that will monitor your application (go figure!)
+if you install the tool along with your app.
+These tools help show *both* CPU problems
+and I/O issues.
+Application performance is very important to businesses,
+so the software industry is full of vendors to choose from
+with a wide range of features.
+
+To see what these tools can be like for free,
+you might want to check out
+{{< extlink "https://www.datadoghq.com/" "Datadog" >}}
+which has a free tier
+(Datadog is not a sponsor,
+I've just used their service,
+enjoyed it,
+and know that it's free
+for a small number of servers).
+Other popular vendors include
+{{< extlink "https://scoutapm.com/" "Scout APM" >}} and
+{{< extlink "https://newrelic.com/" "New Relic" >}}.
+
+Finally,
+we've reached a point where you can diagnose the performance constraints
+on your application using a wide variety
+of tools or services.
+Let's see how to fix problems you may be experiencing!
+
+## Do More
+
+We can address throughput and do more
+by tuning a few different knobs.
+
+When thinking about doing more,
+try to think about the system
+in two different scaling dimensions:
+
+* Horizontally
+* Vertically
+
+Horizontal and vertical scaling are methods
+of describing *how* to do more
+in software systems.
+
+Let's relate this to a silly example
+to give you a good intuitive feel for scaling.
+Imagine that you need to move large bags
+of dirt
+(approximately 40 lbs / 18 kg per bag)
+to plant a huge garden.
+The job is to unload the hundreds of bags
+from a delivery truck
+to your imaginary back yard.
+You enlist the help
+of your friends
+to get the job done.
+
+One strategy is to get your *strongest* friends to help.
+Maybe there aren't as many of them,
+but their strength can make quick work
+of moving the bags.
+This is *vertical scaling*.
+The additional power of your friends allows them
+to move the bags more easily
+than someone with an average or weaker build.
+
+Another strategy is to get *lots* of friends to help.
+Maybe these friends can't move as many bags
+as the stronger ones,
+but many hands make light work.
+This is *horizontal scaling*.
+The increased number of people allows the group
+to move more bags
+because more individuals can do the work simultaneously.
+
+We can apply this same thinking to computer systems.
+
+### Vertical Scaling
+
+To achieve vertical scaling,
+you would run your application on a more powerful computer.
+Cloud vendors give you all kinds of tools
+to do this.
+Picking a faster computer is naturally going to cost you more,
+so vendors make many options available
+(check out
+{{< extlink "https://aws.amazon.com/ec2/instance-types/" "this page from AWS" >}}
+to see the dizzying array of options).
+
+When should you think about vertical scaling?
+One natural case is when your application is CPU bound.
+If the processor is struggling
+to process the requests from an application,
+a faster processor may help.
+With a higher clock speed from a faster individual CPU,
+a computer will be able to process an individual request faster.
+
+Moving to a larger computer is typically considered vertical scaling,
+but it may be possible to have horizontal effects
+by moving to a larger computer
+because of how modern computers are designed.
+These days,
+larger computers typically come
+with a higher number of CPUs.
+Each individual CPU may be faster
+than a smaller computer configuration
+*and* there will be more CPUs
+on the single machine.
+Because of this characteristic,
+you will likely need to change your application configuration
+to take advantage of the additional power
+supplied by the extra CPU cores.
+While the traditional definition of vertical scaling
+(i.e., a faster individual CPU can do work quicker than a slower one) still applies,
+the line between vertical and horizontal scaling is somewhat blurred
+because of the multi-CPU core paradigm
+of modern CPUs.
+
+{{< web >}}
+In the Understand Django deployment article,
+{{< /web >}}
+{{< book >}}
+In the deployment chapter,
+{{< /book >}}
+we discussed Gunicorn's `--workers` flag.
+Recall that Python application servers
+like Gunicorn work
+by creating a main process
+and a set of worker processes.
+The main process will distribute incoming network connections
+to the worker processes to handle the actual traffic
+on your website.
+If you vertically scale the server machine
+from a size that has 1 CPU
+to a machine that has 2, 4, or more CPUs,
+and you don't change the number of workers,
+then you'll waste available CPU capacity
+and won't see most of the benefits
+from the upgrade in server size.
+
+If modern vertical scaling uses more CPUs
+when moving to a bigger machine,
+then what is horizontal scaling?
+The difference is primarily in the number
+of computers needed
+to do the scaling.
+Vertical scaling changes a single machine
+to achieve more throughput.
+Horizontal scaling pulls multiple machines
+into the equation.
+
+### Horizontal Scaling
+
+Conceptually,
+how does horizontal scaling work?
+With the vertical scaling model,
+you can see a clear connection
+between users making a request
+to your website's domain
+and a single machine handling those requests
+(i.e., the main process from your application server
+distributes requests).
+With the horizontal model,
+we're now discussing multiple computers.
+How does a single domain name handle routing
+to multiple computers?
+With more computers!
+
+Like the main process
+that distributes requests,
+we need a central hub
+that is able to route traffic
+to the different machines
+in your horizontally scaled system.
+This hub is usually called a **load balancer**.
+A load balancer can be used
+for multiple things.
+I see load balancers used primarily to:
+
+* route traffic to the different application servers
+ in a system
+* handle the TLS certificate management that makes HTTPS possible
+
+Since the load balancer doesn't do most
+of the actual work
+of processing a request,
+your system can increase its throughput
+by increasing the number
+of application servers.
+In this setup,
+each application server "thinks"
+that it is the main server that's handling requests.
+The load balancer behaves like a client
+that's making requests on behalf
+of the actual user.
+This kind of configuration is called a proxy setup.
+
+If you want to learn more about horizontal scaling
+with a load balancer,
+then I suggest you check out
+{{< extlink "https://www.nginx.com/" "Nginx" >}} (pronounced "engine X"),
+{{< extlink "http://www.haproxy.org/" "HAProxy" >}}
+(which stands for "high availability proxy"),
+or {{< extlink "https://docs.aws.amazon.com/elasticloadbalancing/latest/application/introduction.html" "AWS ALBs" >}}
+(for "application load balancer").
+These tools are commonly used
+and have a reputation for being strong load balancers.
+
+### What's Better?
+
+*What are the tradeoffs between horizontal scaling and vertical scaling?*
+
+When you add more pieces to a system,
+you're increasing the complexity of the system.
+Thus,
+vertical scaling can, at least initially, produce a design
+with lower operational complexity.
+Personally,
+if I ran a service
+on some VPS like Digital Ocean or AWS,
+I would probably reach for vertical scaling first.
+A bigger machine would allow me to use a higher number
+of concurrent worker processes
+to increase throughput,
+and I would avoid the complexity
+of deploying multiple application servers.
+
+In reality,
+I run my side projects on a Platform as a Service, Heroku.
+With my choice of Heroku,
+the service already includes a load balancer by default.
+This means that I can trivially scale horizontally
+by changing a setting in Heroku
+that will start multiple application servers.
+
+While vertical scaling may be a good fit
+if you don't have an existing load balancer,
+that scaling path does have downsides to consider.
+
+First,
+in a vertically scaled world,
+downtime on your server could mean downtime
+*for your service*.
+Whether a site is reachable or not reachable is called "availability"
+by the software industry.
+If your entire site is tied to a large vertically scaled server,
+then it can act as a single point of failure
+if there is a problem.
+
+Secondly,
+a vertically scaled service may potentially have more cost
+for you.
+In my experience,
+most websites have high and low periods
+of usage throughout the day.
+For instance,
+my current employer is a US healthcare company
+that provides telemedicine visits
+for people that need to speak
+with a doctor virtually.
+When it's the middle of the night
+in the US,
+the site utilization is naturally lower
+as most people are sleeping.
+
+One common cost optimization is to use fewer computing resources
+during periods of lower utilization.
+On a vertically scaled service,
+it is harder to change computer sizes quickly.
+Thus,
+that computing resource's usage is relatively fixed,
+even if no one is using your service.
+In contrast,
+a horizontally scaled service can be configured
+to use "auto-scaling."
+
+Auto-scaling is the idea
+that the infrastructure can be resized dynamically,
+depending on the use of the site.
+During periods of high activity,
+more computers can be added automatically
+to join the load balancer distribution
+and handle additional load.
+When activity dies down,
+these extra machines can be removed from use.
+This cost saving technique helps ensure
+that your system is only using what it needs.
+
+The truth is that if your system reaches a large enough size and scale,
+then picking horizontal or vertical scaling is a false choice.
+As a system matures and grows,
+you may need to have a mix of the two scaling types,
+so that your service has the characteristics
+that you need
+(like availability).
+
+I hope that I've helped equip you
+with some mental modeling tools.
+With these tools,
+you should have some idea of how to handle more traffic
+when your site becomes wildly popular. 😄
+
+In this section,
+we focused on increasing throughput
+by changing your service's infrastructure
+to handle more load.
+Now let's shift from the macro point of view
+to the micro view
+and talk about how to improve throughput
+by doing less.
+
+## Do Less
+
+*How do you make your Django site do less work?*
+We should always measure,
+but since I believe most websites are I/O bound,
+let's focus on techniques
+to improve in that dimension.
+
+### Optimizing Database Queries
+
+The most common performance problem
+that I've encountered
+with Django applications
+is the N+1 query bug
+(some people will describe it as the 1+N query bug
+for reasons that may become evident in a moment).
+
+The N+1 bug occurs when your application code calls the database
+in a loop.
+How many queries are in this made up example?
+
+```python
+from application.models import Movie
+
+movies = Movie.objects.all()
+for movie in movies:
+ print(movie.director.name)
+```
+
+It's a bit of a trick question because
+you might have a custom manager (i.e., `objects`),
+but,
+in the simplest scenario,
+there is one query to fetch the movies,
+and one query for each director.
+
+The reason for this behavior is that Django does a lazy evaluation
+of the movies `QuerySet`.
+The ORM is not aware
+that it needs to do a join on the movie and director tables
+to fetch all the data.
+The first query on the movie table occurs
+when the iteration happens
+in the Python `for` loop.
+When the `print` function tries to access the `director` foreign key,
+the ORM does not have the director information cached
+in Python memory
+for the query set.
+Django must then fetch the director data
+in another database query
+to display the director's name.
+
+This adds up to:
+
+* 1 query for the movies table
+* N queries on the director table for each iteration through the `for` loop
+
+Hence the name, "N+1" query bug.
+
+The reason that this is so bad is because calling the database is way slower
+than accessing data in Python memory.
+Also, this problem gets worse
+if there are more rows to iterate over
+(i.e., more movies to process and, thus, more directors to fetch individually).
+
+The way to fix this issue is to hint to Django
+that the code is going to access data
+from the deeper relationship.
+We can do that by hinting to the ORM
+with `select_related`.
+Let's see the previous example with this change.
+
+```python
+from application.models import Movie
+
+movies = Movie.objects.select_related(
+ "director").all()
+for movie in movies:
+ print(movie.director.name)
+```
+
+In the reworked example,
+the ORM will "know" that it must fetch the director data.
+Because of this extra information,
+the framework will fetch from both the movie and director tables
+*in a single query*
+when the `for` loop iteration starts.
+
+Under the hood,
+Django performs a more complex `SELECT` query
+that includes a join
+on the two tables.
+The database sends back all the data at once
+and Django caches the data
+in Python memory.
+Now,
+when execution reaches the `print` line,
+the `director.name` attribute can pull from memory
+instead of needing to trigger another database query.
+
+The performance savings here can be massive,
+especially if your code works with a lot of database rows
+at once.
+
+While `select_related` is fantastic,
+it doesn't work for all scenarios.
+Other types of relationships
+like a many to many relationship can't be fetched
+in a single query.
+For those scenarios,
+you can reach for `prefetch_related`.
+With this method,
+Django will issue a smaller number of queries
+(usually 1 per table)
+and blend the results together in memory.
+In practice,
+`prefetch_related` operates very much like `select_related`
+in most circumstances.
+Check out the Django docs to understand more.
+
+### Caching Expensive Work
+
+If you know:
+
+* execution will likely happen many times
+* is expensive to create, AND
+* won't need to change
+
+then you're looking at work
+that is a very good candidate to cache.
+With caching,
+Django can save the results
+of some expensive operation
+into a very fast caching tool
+and restore those results later.
+
+A good example of this might be a news site.
+A news site is very "read heavy,"
+that is,
+users are more likely to use the site
+for viewing information
+than for writing and saving information
+to the site.
+A news site is also a good example
+because users will read the same article,
+and the content of that article is fixed in form.
+
+Django includes tools to make it simple
+to work with the cache
+to optimize content like our news site example.
+
+The simplest of these tools is the `cache_page` decorator.
+This decorator can cache the results
+of an entire Django view
+for a period of time.
+When a page doesn't have any personalization,
+this can be a quick and effective way
+to serve HTML results
+from a view.
+You can find this decorator
+in `django.views.decorators.cache`.
+
+You may need a lower level of granularity
+than a whole page.
+For instance,
+your site might have some kind of logged in user
+and a customized navigation bar
+with a profile picture or something similar.
+In that scenario,
+you can't really cache the whole page
+and serve that to multiple users,
+because other users would see the customized navigation bar
+of the first user who made the request.
+If this is the kind of situation you're in,
+then the `cache` template tag may be the best tool for you.
+
+Here's a template example of the `cache` tag in use.
+
+{{< web >}}
+```django
+{% load cache %}
+
+Hi {{ user.username }}, this part won't be cached.
+
+{% cache 600 my_cache_key_name %}
+ Everything inside of here will be cached.
+ The first argument to `cache` is how long this should be cached
+ in seconds. This cache fragment will cache for 10 minutes.
+ Cached chunks need a key name to help the cache system
+ find the right cache chunk.
+
+ This cache example usage is a bit silly because this is static text
+ and there is no expensive computation in this chunk.
+{% endcache %}
+```
+{{< /web >}}
+{{< book >}}
+```djangotemplate
+{% load cache %}
+
+Hi {{ user.username }}, this part won't be cached.
+
+{% cache 600 my_cache_key_name %}
+ Everything inside of here will be cached.
+ The first argument to `cache` is how long this should be cached
+ in seconds. This cache fragment will cache for 10 minutes.
+ Cached chunks need a key name to help the cache system
+ find the right cache chunk.
+
+ This cache example usage is a bit silly because this is static text
+ and there is no expensive computation in this chunk.
+{% endcache %}
+```
+{{< /book >}}
+
+With this scheme,
+any expensive computation
+that your template does will be cached.
+*Be careful with this tag!*
+The tag is useful if computation happens during rendering,
+but if you're doing the evaluation and fetching *inside
+of your view*
+instead of at template rendering time,
+then you're unlikely to get the benefits that you want.
+
+Finally,
+there is the option to use the cache interface directly.
+Here's the basic usage pattern:
+
+```python
+# application/views.py
+from django.core.cache import cache
+
+from application.complex import calculate_expensive_thing
+
+def some_view(request):
+ expensive_result = cache.get(
+ "expensive_computation")
+ if expensive_result is None:
+ expensive_result = calculate_expensive_thing()
+ cache.set(
+ "expensive_computation",
+ expensive_result
+ )
+
+ # Handle the rest of the view.
+ ...
+```
+
+On the first request to this view,
+the `expensive_result` won't be in the cache,
+so the view will calculate the result
+and save it to the cache.
+On subsequent requests,
+the expensive result can be pulled
+from the cache.
+In this example,
+I'm using the default timeout
+for the cache,
+but you can control the timeout values
+when you need more control.
+The cache system has plenty
+of other cool features,
+so check it out in the docs.
+
+As fair warning,
+caching often requires more tools and configuration.
+Django works with very popular cache tools
+like Redis and Memcached,
+but you'll have to configure one of the tools
+on your own.
+The Django documentation will help you,
+but be prepared for more work on your part.
+
+Database optimization and caching are go-to techniques
+for optimization.
+When you're optimizing,
+how do you know that you're doing it right?
+What gains are you getting?
+Let's look at some tools next
+that will let you answer those questions.
+
+## Tools To Measure Change
+
+We'll look at tools at an increasing level of complexity.
+This first tool is one
+that is massively useful
+while developing in Django.
+The other tools are more general purpose tools,
+but they still are worth knowing about,
+so that you'll know when to reach for them.
+
+Each of these tools helps you get real performance data.
+By measuring the before and after
+of your changes,
+you can learn if the changes are actually producing the gains
+that you expect or hope to achieve.
+
+### Django Debug Toolbar
+
+The
+{{< extlink "https://django-debug-toolbar.readthedocs.io/en/latest/index.html" "Django Debug Toolbar" >}}
+is a critical tool
+that I add to my projects.
+The toolbar acts as an overlay
+on your site
+that opens to give you a tray
+of different categories
+of diagnostic information
+about your views.
+
+Each category of information is grouped
+into a "panel,"
+and you can select between the different panels
+to dig up information
+that will assist you while doing optimization work.
+
+You'll find panels like:
+
+* SQL
+* Templates
+* Request
+* Time
+
+The SQL panel is where I spend nearly all
+of my time when optimizing.
+This panel will display all the queries
+that a page requests.
+For each query,
+you can find what code triggered the database query
+and you even get the exact SQL `SELECT`.
+You can also get an `EXPLAIN` about a query
+if you really need the gory details
+of what the database is doing.
+
+With a little bit of eye training,
+you'll learn to spot N+1 query bugs
+because you can see certain queries repeated over and over
+and "cascading" like a waterfall.
+
+I'll often test with the debug toolbar
+when I'm trying to sprinkle in `select_related`
+to visually confirm
+that I've reduced the query count on a page.
+The debug toolbar is open source
+and is a great free resource.
+The toolbar is totally worth the investment
+of configuring it
+for your next Django project.
+
+### hey / ab
+
+There are two tools
+that are very similar
+that I use when I need to get a crude measure
+of the performance of a site.
+These tools are
+{{< extlink "https://github.com/rakyll/hey" "hey" >}}
+and
+{{< extlink "https://httpd.apache.org/docs/2.4/programs/ab.html" "ab" >}}
+(Apache Bench).
+Both of these tools are *load generators*
+that are meant to benchmark a site's basic performance characteristics.
+
+In practice,
+I prefer `hey`,
+but I mention `ab`
+because it is a well known tool in this space
+that you are likely to encounter
+if you research this load generator topic.
+
+Operating this kind of tool is trivial:
+
+```bash
+$ hey https://www.example.com
+```
+
+In this example,
+hey will try to open up a large number
+of concurrent connections
+to the URL
+and make a bunch of requests.
+When the tool is done,
+it will report how many
+of the requests were successful
+and some related timing information
+and statistics.
+Using a load generator like this lets you synthesize traffic
+to learn how your site is going to perform.
+
+I'd suggest you be careful
+where you tell these tools to operate.
+If you're not careful,
+*you could cause a Denial of Service attack
+on your own machines.*
+The flood of requests might make your site unavailable
+to other users
+by consuming all your server's resources.
+Think twice before pointing this at your live site!
+
+### Locust
+
+The previous load generator tools
+that I mentioned act as somewhat crude measurements
+because you're limited
+to testing a single URL
+at a time.
+What should you do if you need to simulate traffic
+that matches real user usage patterns?
+Enter
+{{< extlink "https://locust.io/" "Locust" >}}.
+Locust is not a tool that I would reach for casually,
+but it is super cool and worth knowing about.
+
+The goal of Locust is to do load testing
+on your site
+in a realistic way.
+This means that it's your job to model the expected behavior
+of your users
+in a machine understandable way.
+If you know your users well
+(and I hope you do),
+then you can imagine the flows
+that they might follow
+when using your site.
+
+In Locust,
+you codify the behavior patterns
+that you care about,
+then run Locust
+to simulate a large number of users
+that will act like you expect
+(with randomness to boot
+to really make the test like reality).
+
+Advanced load testing is something you may never need
+for your site,
+but it's pretty cool to know
+that Python has you covered
+if you need to understand performance
+and the limits
+of your site
+at that deep level.
+
+### Application Performance Monitoring (APM)
+
+{{< web >}}
+Earlier in this article,
+{{< /web >}}
+{{< book >}}
+Earlier in this chapter,
+{{< /book >}}
+I mentioned that Application Performance Monitoring tools
+can show you CPU and memory utilization
+of your site.
+That's usually just the tip of the iceberg.
+
+An APM tool often goes far beyond hardware resource measurement.
+I like to think
+of APMs
+as a supercharged version
+of the debug toolbar.
+
+First, an APM is used on live sites typically.
+The tool will collect data about real requests.
+This lets you learn about the *real* performance problems
+on the site
+that affect *real* users.
+
+For instance,
+New Relic will collect data
+on slow requests into "traces."
+These traces are aggregated
+into a set to show you which pages
+on your site
+are the worst performers.
+You can drill into that list,
+view an individual trace,
+and investigate the problem.
+
+Maybe you've got an N+1 bug.
+Maybe one of your database tables is missing an index
+on an important field,
+and the database is scanning too many records
+during `SELECT` statements.
+These traces (or whatever they are called in other services) help you prioritize
+what to fix.
+
+In fact,
+an APM highlights the true value of measurement.
+If I can leave you with a parting thought about optimization,
+think about this:
+***optimize where it counts***.
+
+Here's a simple thought experiment to illustrate what I mean.
+You have an idealized system that does two things repeatedly:
+
+* One task (A) is 90% of all activity on the site.
+* The other task (B) is the remaining 10%.
+
+If you have to pick a target to try to optimize
+because your system performance is inadequate,
+which one do you pick?
+
+Let's assume that you know an optimization for each type of task
+that could cause the task to execute in 50% of the time.
+If implementing each optimization is the same level of effort,
+then there is a clear winner as to which task you should optimize.
+You could either:
+
+* Optimize A for 90% * 50% for a total system saving of 45%.
+* Optimize B for 10% * 50% for a total system saving of 5%.
+
+In most circumstances,
+spend your optimization effort
+on the area that will have outsized impact
+(i.e., pick task A as much as you can).
+Sometimes the hard part is figuring out which task is A
+and which task is B.
+Monitoring tools like an APM can help you see
+where the worst offenders are
+so you can focus your limited time in the right spot.
+
+## Summary
+
+{{< web >}}
+In this article,
+{{< /web >}}
+{{< book >}}
+In this chapter,
+{{< /book >}}
+we looked into making Django apps go fast.
+We saw:
+
+* A mental model for thinking about performance optimization
+* Different types of performance bottlenecks
+* How to get your system to do more by either horizontal or vertical scaling
+* How to get your app to do less work
+ by optimizing database queries and caching
+* Tools to aid you in all of this optimization work
+
+{{< web >}}
+In the next article,
+{{< /web >}}
+{{< book >}}
+In the next chapter,
+{{< /book >}}
+we'll look into security.
+You'll learn about:
+
+* How Django helps you be more secure with some of its design features
+* What those different warnings from `./manage.py check --deploy` mean
+* Fundamental things you should consider to help keep your site secure
+
+{{< web >}}
+If you'd like to follow along
+with the series,
+please feel free to sign up
+for my newsletter
+where I announce all of my new content.
+If you have other questions,
+you can reach me online
+on Twitter
+where I am
+{{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
+{{< /web >}}
+
diff --git a/content/understand-django/2022-03-10-secure-apps.pt.md b/content/understand-django/2022-03-10-secure-apps.pt.md
new file mode 100644
index 00000000..97a73fb1
--- /dev/null
+++ b/content/understand-django/2022-03-10-secure-apps.pt.md
@@ -0,0 +1,622 @@
+---
+title: "Security And Django"
+description: >-
+ You want to protect your users' privacy, right?
+ The goal is noble
+ and users demand it,
+ but how do you do it?
+ In this Understand Django article,
+ we'll look at some areas
+ that improve the security
+ of your application.
+image: img/django.png
+type: post
+categories:
+ - Python
+ - Django
+tags:
+ - Python
+ - Django
+ - security
+series: "Understand Django"
+
+---
+
+In the last
+{{< web >}}
+[Understand Django]({{< ref "/understand-django/_index.md" >}})
+article,
+{{< /web >}}
+{{< book >}}
+chapter,
+{{< /book >}}
+we learned about where apps slow down.
+We explored techniques that help sites handle the load
+and provide a fast experience for users.
+
+{{< web >}}
+With this article,
+{{< /web >}}
+{{< book >}}
+With this chapter,
+{{< /book >}}
+we will look at security.
+How does a Django site stay safe
+on the big, bad internet?
+Let's find out.
+
+{{< understand-django-series "security" >}}
+
+## A Security Confession
+
+I have a confession to make.
+Of all the topics
+that I've covered about Django
+{{< web >}}
+in this series,
+{{< /web >}}
+{{< book >}}
+in this book,
+{{< /book >}}
+*this is my least favorite one.*
+{{< web >}}
+Perhaps that's why I've pushed the subject
+so far into this list of articles.
+{{< /web >}}
+
+I have a very hard time getting excited about security
+because it feels like a pure cost to me.
+As developers,
+we're in this arms race against malicious people
+who want to steal and profit
+from the data of others.
+In a perfect world,
+everyone would respect the privacy of others
+and leave private data alone.
+Alas,
+the world is far from perfect.
+
+The bad actors have devised clever and tricky methods
+of exploiting websites
+to steal data.
+Because of this,
+application developers have to implement guards
+in an attempt to prevent these exploits.
+Implementing those guards detract
+from the main objective
+of site building
+and often feels like a drag on efficiency.
+
+All that being said,
+**security is super important**.
+Even if you're like me
+and the topic doesn't naturally interest you
+(or actively feels like a waste of time),
+the security of your application matters.
+
+* Privacy matters.
+* Trust matters.
+
+If we cannot protect the information
+that users of our Django sites bring,
+then trust will rapidly erode
+and, most likely,
+your users will disappear along with it.
+
+As noted in this section,
+security is not my favorite topic.
+I'm going to describe some security topics
+as they relate to Django,
+but if you want to learn from people who *love* security,
+then I would recommend reading
+from the
+{{< extlink "https://owasp.org/" "Open Web Application Security Project" >}}.
+This popular group can teach you far more
+about security
+than I can,
+and do it with gusto!
+
+## The Three **C**s
+
+Learning about security involves learning a bunch of acronyms.
+I don't know if this is something that security researchers
+like to do,
+or if the reason is because the problems
+that the acronyms stand for are challenging
+to understand.
+Either way,
+let's look at three common acronyms that start with C
+and the problems they address.
+
+### CSRF
+
+{{< web >}}
+In a number of these Understand Django articles,
+{{< /web >}}
+{{< book >}}
+In a number of these chapters,
+{{< /book >}}
+I have discussed CSRF briefly.
+{{< web >}}
+In the forms article,
+{{< /web >}}
+{{< book >}}
+In the forms chapter,
+{{< /book >}}
+I did some hand waving and stated
+that you need a CSRF token
+for security reasons
+and basically said "trust me" at the time.
+
+CSRF stands for *Cross Site Request Forgery*.
+In simple terms,
+a CSRF attack allows an attacker to use someone's credentials
+to a different site
+without their permission.
+With a bit of imagination,
+you can see where this goes:
+
+* Attacker socially manipulates a user to click a link.
+* The click activity exploits the user's credentials to a site
+ and changes something about the user's account
+ like their email address.
+* The attacker changes the email address to something they control.
+* If the original site is something like an e-commerce site,
+ the attacker may make purchases
+ using the user's stored credit card information.
+
+Django includes a capability to help thwart this kind of attack.
+Through the use of **CSRF tokens**,
+we can help prevent bad actors
+from performing actions
+without user consent.
+
+A CSRF token works by including a generated value
+that gets submitted along with the form.
+The template looks like:
+
+{{< web >}}
+```django
+
+```
+{{< /web >}}
+{{< book >}}
+```djangotemplate
+
+```
+{{< /book >}}
+
+When this renders,
+the result would be something like:
+
+```html
+
+```
+
+The value would naturally be different
+from my example.
+When the form is submitted,
+the CSRF token gets checked for validity.
+A valid CSRF token is required
+to make a `POST` request,
+so this level of checking can help prevent attackers
+from changing a user's data
+on your site.
+
+You can learn more about CSRF
+with Django's
+{{< extlink "https://docs.djangoproject.com/en/4.1/ref/csrf/" "Cross Site Request Forgery protection" >}}
+reference page.
+
+### CORS
+
+Imagine that you've built `myapp.com`.
+For your user interface,
+instead of Django templates,
+you built a client UI
+using a JavaScript framework
+like {{< extlink "https://vuejs.org/" "Vue.js" >}}.
+Your application is serving static files
+at `myapp.com`,
+and you built a Django-powered API
+that is handling the data
+which gets called at `api.myapp.com`.
+
+In this scenario,
+browsers will require you to set up CORS.
+CORS is *Cross-Origin Resource Sharing*.
+The goal of CORS is to help protect a domain
+from undesirable access.
+
+In our example,
+your API at `api.myapp.com` may only be designed
+to work with the user interface
+at `myapp.com`.
+With CORS,
+you can configure your API
+so that it will reject any requests
+that do not come
+from the `myapp.com` domain.
+This helps prevent bad actors
+from using `api.myapp.com`
+in the browser.
+
+Django does *not* include tools
+to handle CORS configuration
+from the core package.
+To make this work,
+you'll need to reach for a third party package.
+Since CORS configuration is handled
+through HTTP headers,
+you'll find that the very appropriately named
+{{< extlink "https://github.com/adamchainz/django-cors-headers" "django-cors-headers" >}} package
+is exactly what you need.
+
+I won't walk through the whole setup
+of that package
+because the README does a good job
+of explaining the process,
+but I will highlight the crucial setting.
+With django-cors-headers,
+you need to set the `CORS_ALLOWED_ORIGINS` list.
+Anything not in that list will be blocked
+by CORS controls in the browser.
+Our example configuration would look like:
+
+```python
+CORS_ALLOWED_ORIGINS = [
+ "https://myapp.com",
+]
+```
+
+As you read about CORS on the internet,
+you'll probably run into advice to set the HTTP header
+of `Access-Control-Allow-Origin: *`.
+This wildcard is what you'll get if you set `CORS_ALLOW_ALL_ORIGINS = True`
+in django-cors-headers.
+*This is probably not what you really want.
+Using this feature opts your site out of CORS protection.*
+Unless you have some public web API
+that is *designed* to work
+for many domains,
+you should try to avoid opting out of CORS.
+
+CORS is not a core concept that you will find in Django.
+If you want to learn more about the specifics of CORS,
+check out
+{{< extlink "https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS" "Cross-Origin Resource Sharing (CORS)" >}}
+from the Mozilla Developer Network (MDN).
+
+### CSP
+
+The final **C** in our tour
+is *Content Security Policy* or CSP, for short.
+You might roughly think of CSP
+as the inverse of CORS.
+Where CORS defines what parts of the internet can access your domain,
+CSP defines what your domain can access from the internet.
+
+The goal of CSP is to protect users on your site
+from running JavaScript
+(and other potentially harmful resources like images)
+from places that you don't want.
+
+To understand how your site can be vulnerable
+to these kinds of attacks,
+we need to understand a well-known attack vector
+called Cross-Site Scripting (XSS).
+XSS is when a bad actor finds a way to run a script
+on your domain.
+Here's a classic way that XSS can happen:
+
+* A site has a form that accepts text data.
+* Then the site displays that text data *in its raw form*.
+
+At first,
+that seems harmless.
+
+```html
+
+ What could possibly go wrong?
+
+```
+
+For an honest interaction like "What could possibly go wrong?"
+as user input,
+that is truly harmless.
+What about this?
+
+```html
+
+ I am getting sneaky .
+
+```
+
+Now,
+the user added a bit of HTML markup.
+Again,
+this is fairly benign
+and will only add some unanticipated italics.
+What if the user is a bit more clever than that?
+
+```html
+
+
+
+```
+
+Here's where a site gets into trouble.
+If this is rendered on a page,
+a little alert box will appear.
+You can imagine this happening in a forum
+or some other sharing site
+where multiple people will see this output.
+That's annoying,
+but it's still not horrible.
+
+What does a really bad scenario look like?
+A really bad scenario is where the bad guys figure out
+that your site is unsafe in this way.
+Consider this:
+
+```html
+
+
+
+```
+
+Now your users are really in trouble.
+In this final version,
+the bad guys won.
+A user's browser will download and execute whatever JavaScript
+is in `owned.js`.
+This code could do all kinds of stuff
+like using the `fetch` API
+to run AJAX requests
+that can change the user's account credentials
+and steal their account.
+
+How do we defend against this kind of attack?
+There isn't a singular answer.
+In fact,
+multiple layers of protection is often what you really want.
+In security,
+this idea is called "defense-in-depth."
+If you have multiple layers to protect your site,
+then the site may become a less appealing target
+for attackers.
+
+For this particular scenario,
+we can use a couple of things
+
+* HTML escaping of untrusted input
+* CSP
+
+The real problem above is that the site is rendering user input
+without any modification.
+This is a problem with HTML
+because the raw characters are interpreted as HTML code
+and not just user data.
+
+The simplest solution is to make sure that any characters
+that mean something specific to HTML
+(like `<` or `>`)
+are replaced with escape codes (`<` or `>`)
+that will display the character
+in the browser
+without treating it like the actual HTML code character.
+**Django does this auto-escaping of user data by default**.
+You can disable this behavior for portions
+of a template
+using a variety of template tags
+like `autoescape` and the (ironically named?) `safe` tag.
+
+Because there are ways to opt out
+of safe behavior from HTML escaping
+and because clever attackers might find other ways
+to inject script calls
+into your site,
+CSP is another layer of protection.
+
+Primarily, CSP is possible
+with a `Content-Security-Policy` HTTP header.
+You can read all of the gritty details
+on the
+{{< extlink "https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP" "Content Security Policy (CSP)" >}}
+article on MDN.
+Like CORS,
+CSP is not something that Django supports out-of-the-box.
+Thankfully, Mozilla (yep, the same Mozilla from MDN),
+offers a
+{{< extlink "https://django-csp.readthedocs.io/en/latest/index.html" "django-csp" >}} package
+that you can use to configure an appropriate policy
+for your Django site.
+
+In a content security policy,
+you mark everything that you want to allow.
+This fundamentally changes what requests your site will connect to.
+Instead of allowing everything by default,
+the site operates on a model that denies things by default.
+With a "deny by default" stance,
+you can then pick resources
+which you deem are safe for your site.
+Modern browsers respect the policy declared
+by the HTTP header
+and will refuse to connect to resources
+outside of your policy
+when users visit your site at your domain.
+
+There is something obvious
+that we should get out of the way.
+This setup and configuration requires *more work*.
+Having more secure systems requires effort,
+study,
+and plenty of frustration
+as you make a site more secure.
+The benefits to your users or customers is
+that their data stays safe.
+I think most people expect this level of protection
+by default.
+
+So,
+do you have to become a security expert
+to build websites?
+I don't think so.
+There is a set of fundamental issues
+with web application security
+that you should know about
+(and we've covered some of those issues already),
+but you don't need to be prepared
+to go to black hat conventions
+in order to work on the web.
+
+{{< web >}}
+Before finishing up this security article,
+{{< /web >}}
+{{< book >}}
+Before finishing up this security chapter,
+{{< /book >}}
+let's look at what Django provides
+so that you can be less of a security expert
+and use the knowledge
+of the community.
+
+## Check Command Revisited
+
+The Django documentation includes a good overview
+of the framework's security features
+on the
+{{< extlink "https://docs.djangoproject.com/en/dev/topics/security/" "Security in Django" >}} page.
+
+Aside from the content outlined
+on the security page,
+we can return to the check command
+{{< web >}}
+discussed in previous articles.
+{{< /web >}}
+{{< book >}}
+discussed in previous chapters.
+{{< /book >}}
+Recall that Django includes a `check` command
+that can check your site's configuration
+before you deploy a site live.
+The command looks like:
+
+```bash
+$ ./manage.py check --deploy
+```
+
+The output from this command can show
+where your configuration is less than ideal
+from a security perspective.
+
+The security warnings that come
+from running the `check` command are defined
+in `django.core.checks.security`.
+A more readable version
+of the available security checks is
+on the
+{{< extlink "https://docs.djangoproject.com/en/4.1/ref/checks/#security" "System check framework" >}}
+reference page.
+
+Scanning through the list
+of checks,
+you'll find that
+
+* many checks center around configuring your site
+ to run with HTTPS.
+ Secure connections used to reference SSL
+ for Secure Sockets Layer.
+ Along the way,
+ that layer changed names to TLS
+ for Transport Layer Security.
+ In practice,
+ if you see either of those terms,
+ think `https://`.
+* other checks confirm that your site has the kinds
+ of
+ {{< extlink "https://docs.djangoproject.com/en/4.1/ref/middleware/#security-middleware" "middleware" >}}
+ installed
+ that offer some
+ of the protection discussed previously
+ (like CSRF).
+* still other checks look for core settings
+ that should be set
+ like `DEBUG = False`
+ and defining the `ALLOWED_HOSTS` setting.
+
+There is a good comment in the Django security checks reference docs
+that is worth repeating here:
+
+> The security checks do not make your site secure.
+They do not audit code, do intrusion detection, or do anything particularly complex.
+Rather, they help perform an automated, low-hanging-fruit checklist,
+that can help you to improve your site’s security.
+
+When you're thinking through security,
+do some homework
+and don't let your brain go on autopilot.
+Remember that users develop a trust relationship
+with your websites.
+That trust is easy to break
+and may cause people to leave your site forever.
+
+## Summary
+
+{{< web >}}
+In this article,
+{{< /web >}}
+{{< book >}}
+In this chapter,
+{{< /book >}}
+we explored security topics
+and how they relate to Django.
+We covered:
+
+* CSRF
+* CORS
+* CSP
+* Cross-site scripting (XSS)
+* The security checks and information available
+ from the `check` command
+
+{{< web >}}
+In the *last* article
+of the Understand Django series,
+{{< /web >}}
+{{< book >}}
+In the *last* chapter,
+{{< /book >}}
+we'll get into debugging.
+You'll learn about:
+
+* Debugging tools like `pdb`
+* Browser tools
+* Strategies for finding and fixing problems
+
+{{< web >}}
+If you'd like to follow along
+with the series,
+please feel free to sign up
+for my newsletter
+where I announce all of my new content.
+If you have other questions,
+you can reach me online
+on Twitter
+where I am
+{{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
+{{< /web >}}
+
diff --git a/content/understand-django/2022-05-31-debugging-tips-techniques.pt.md b/content/understand-django/2022-05-31-debugging-tips-techniques.pt.md
new file mode 100644
index 00000000..39da3d31
--- /dev/null
+++ b/content/understand-django/2022-05-31-debugging-tips-techniques.pt.md
@@ -0,0 +1,767 @@
+---
+title: "Debugging Tips And Techniques"
+description: >-
+ Your Django app is up.
+ You've got users.
+ Your users are hitting bugs.
+ How do you debug
+ to fix the problems?
+ That's the focus of this Understand Django article.
+image: img/django.png
+type: post
+categories:
+ - Python
+ - Django
+tags:
+ - Python
+ - Django
+ - debugging
+series: "Understand Django"
+
+---
+
+In the last
+{{< web >}}
+[Understand Django]({{< ref "/understand-django/_index.md" >}})
+article,
+{{< /web >}}
+{{< book >}}
+chapter,
+{{< /book >}}
+we looked at security.
+How does a Django site stay safe
+on the big, bad internet?
+{{< web >}}
+The article explored some core elements
+{{< /web >}}
+{{< book >}}
+The chapter explored some core elements
+{{< /book >}}
+for making a Django app more secure.
+
+{{< web >}}
+With this article,
+{{< /web >}}
+{{< book >}}
+With this chapter,
+{{< /book >}}
+we will investigate problem solving techniques
+for Django apps.
+The goal is to equip you with tools
+to fix the real problems
+that you'll hit when building your Django site.
+
+{{< understand-django-series "debugging" >}}
+
+## Systematic Problem Solving
+
+When you have real users using your website
+and they report problems with the site,
+what can you do to fix the problems?
+Having a mental framework
+for how to fix problems is really useful,
+because it provides a repeatable process
+that you'll be able to use as long
+as you're building on the web.
+Let's discuss the mental framework
+that I use,
+which I think of as *systematic problem solving*.
+
+The first thing to drill into your head is
+that computers are deterministic.
+Given a set of inputs,
+they will produce a repeatable output.
+This is crucial to remember
+because the art of problem solving
+with computers
+is about sleuthing the state
+of your system
+(i.e., a Django website in this context)
+to determine why a given set of inputs
+produced an undesirable output,
+also known as a *bug*.
+
+When debugging a problem,
+the challenge is figuring out what all those inputs are.
+The inputs can vary wildly.
+Successful problem solvers are those
+who can find the inputs
+that caused the bad output.
+
+This is the core of systematic problem solving.
+With systematic problem solving,
+you want repeatable patterns
+that help you build up a mental model
+of how a problem occurred.
+Once you understand the problem,
+you'll be ready to create the solution
+and fix your bug.
+
+*How do you repeatably figure out problems with a website?*
+
+In my experience,
+the most fruitful way to understand a problem is to reproduce it.
+To reproduce a problem requires context and data
+about what happened in the first place.
+
+What are the sources of this data?
+
+{{< web >}}
+* Error monitoring services like Rollbar or Sentry can be a fantastic source
+ of data.
+* Log data, which we'll discuss later in this article, can be another excellent source.
+{{< /web >}}
+{{< book >}}
+* Error monitoring services like Rollbar or Sentry can be a fantastic source
+ of data.
+* Log data, which we'll discuss later in this chapter, can be another excellent source.
+{{< /book >}}
+
+Error monitoring services help show what happened.
+These services will collect tracebacks of Python exceptions
+and display them in context
+of a particular request.
+Some of these tools can even show the sequence of events
+that led to the failure case.
+
+My favorite strategy for cases like this is to produce
+an automated test
+that can trigger the same exception
+as the one reported by the error monitoring service.
+This is often a mechanical transformation
+of the data provided on an error report page
+into the setup data
+of a unit test.
+
+The great part about having a unit test is that you now have something
+that you can repeat with ease.
+This is a lot better than clicking around on your site
+and trying to recreate a problem manually.
+Because you've captured the problem scenario in test code,
+you can know *exactly* when you've **fixed** the problem.
+You can work on the site's code and run the test over and over
+until you've devised a solution
+that overcomes your error.
+
+This process is a systematic approach
+that you can apply again and again
+to solve problems.
+The process in a nutshell looks like:
+
+* Collect data from the failure scenario
+* Transform that data into an automated test
+ that demonstrates the problem in a repeatable fashion.
+* Fix your site's code until the test passes
+ and the problem is resolved.
+
+The secondary benefit of this systematic model is
+that your site has a passing unit test when you're done.
+This test acts as a guard against future failures
+of a similar kind (i.e., a "regression" test).
+
+I don't want to oversell the value
+of these kinds of tests.
+Regression tests can be useful,
+but in my experience,
+I've rarely seen regression tests fail
+after the offending code is fixed.
+Nonetheless,
+if the test is fast enough,
+these kinds of tests are worth keeping
+in your automated test suite
+for those rare cases where you *do* encounter a regression failure.
+
+Maybe my unit testing approach doesn't appeal to you.
+That's fine.
+Do what works well for you.
+My larger point is that you should try to take a systematic approach
+to problem solving.
+**Find repeatable patterns that you can apply to your context.**
+
+Approaching problems with a process in hand helps you avoid feeling stuck.
+Having methods for solving problems also helps
+when the pressure is on,
+and you're trying to fix a critical and time sensitive bug
+for your customers.
+Not knowing what to do in a high pressure scenario can add even more stress
+to an already stressful situation.
+
+## `print` Without Shame
+
+In the previous section,
+I wrote "Fix your site's code until the test passes,"
+but I didn't explain how you'd actually do that.
+
+When you're working with a failing test
+and you're trying to make it pass
+to solve a problem,
+you have to understand the scenario
+at a pretty deep level.
+
+One tool to reach for is the `print` function.
+You can sprinkle some calls to `print`
+into the code you're working with
+to gain an understanding
+of what is happening.
+
+In your journey in programming,
+you'll run across this idea
+that "print debugging" is a bad idea.
+Not everyone espouses this idea,
+but it exists out there.
+In the same breath,
+you'll read that you should be using a debugger instead
+or some other tool.
+
+Debuggers,
+which are great tools
+that we'll cover shortly,
+are not the only tool worth knowing.
+In fact,
+in many cases,
+debuggers are a terrible tool for the job.
+
+What does the `print` function do
+that makes it so useful?
+**`print` provides a chronology.**
+
+With the `print` function,
+you can observe changes *over time*.
+The `print` function can answer questions like:
+
+* What is the value of my variable
+ in each iteration through the loop?
+* Is this block of code even reached
+ by the interpreter?
+* What is the before and after state
+ after a section of code executes?
+
+The nice part about using `print`
+in an automated test is that you can see all the data,
+all at once.
+The results of a handful of `print` calls starts
+to tell a story
+of what happened during execution.
+If your "story" isn't rich enough and meaningful enough
+to understand what's happening,
+you add more `print` calls
+and run the test again.
+
+I'd estimate that I am able to use `print`
+for 70-80% of my debugging needs.
+
+What should you print?
+
+* One simple strategy is to print a number sequence.
+ Adding `print(1)`, `print(2)`, or more
+ to various lines
+ can show what parts of code are executing
+ or what patterns are happening in loops.
+* Sometimes I'll print the values of conditionals
+ for branches that I'm interested in.
+ This helps me figure out when certain branches
+ in the code are taken versus when they are not.
+* Other times I'll add prints
+ with a bunch of newlines
+ to visually separate things.
+ `print('\n\nHERE\n\n')` is surprisingly effective.
+
+The answer to "what should you print?" is really
+"what do you want to know?"
+As you use `print`,
+you'll develop a good feel
+for what information you find most useful.
+
+Tip: As of Python 3.8,
+you can print the variable name
+in an f-string along with its value:
+
+```python
+>>> foo = 5
+>>> print(f'Hello world {foo=}')
+Hello world foo=5
+```
+
+`print` is pretty great,
+but it isn't always my tool
+of choice.
+For instance, `print` is not the best tool
+when problem solving a live site.
+`print` also doesn't work as well
+when I need to get really detailed information
+about the state of my program.
+
+When do I reach for something else?
+Let's look at debuggers next.
+
+## Debuggers
+
+If 70-80% of the time I can get away with `print`,
+what about the rest of the time?
+
+Sometimes I'll use a debugger.
+A debugger is a specialized tool
+that allows you to go through your Python code,
+one line at a time.
+
+Considering how much code we write
+and how much we use from other packages,
+this means that a debugger can be a slow tool
+to utilize.
+A debugger makes up for this slowness
+with an unparalleled amount
+of information
+about what is going on
+at the exact moment
+that the Python interpreter executes a line of code.
+
+How do you use a debugger?
+
+Assuming that you're following my strategy
+of writing an automated test
+when you're working on a problem,
+the simplest way to start a debugger is
+by adding `breakpoint()`
+before the code you want to check.
+
+In a standard Python installation,
+adding this function call will pause your program
+by running the Python debugger, `pdb`,
+starting from the call to `breakpoint`.
+
+If you do this, you'll be left at a prompt
+that starts with `(Pdb)`.
+From here,
+you'll need to know some commands
+to navigate within the debugger.
+I'll cover the primary commands I use here,
+but you can type `h`
+to see a list of the available commands
+with instructions for how to get more help info.
+
+My natural inclination in the debugger is to know where I am.
+The `l` command will *list* code
+that the interpreter is about to execute,
+along with an arrow showing the next line
+that Python will run.
+I may also want to know where I am in the call stack
+(i.e., the history of calls that go back all the way
+to the main function
+that started the Python process).
+By using the `w` command to show *where* I am,
+pdb will show the call stack
+with the current line of code listed last
+by the prompt
+and the oldest function listed
+at the top of the output.
+These two commands give me the context
+at any particular moment.
+
+Next,
+I'll often want to know the values
+of local variables
+when I'm debugging.
+I can either use `p` to *print* the value
+or `pp` to *pretty print*
+in cases when I have a structure like a list or dictionary.
+
+All the previous commands orient me
+to where my code is and what values are in the data structures.
+With that context,
+I'm ready to work through my code
+with two additional commands.
+In a debugger,
+you advance the Python interpreter a line at a time.
+There are two styles to do this.
+
+One way is with the `n` command
+to go to the *next* line.
+This command is what you want
+when you don't really care about how a line runs.
+For instance,
+if the line calls a function that you know works,
+`n` is the way to pass that line
+to let it execute in its entirety.
+
+The other command to advance the interpreter is the `s` command.
+The `s` command lets you *step* through the code
+at the smallest possible increment.
+That means if you're on a line
+with a function call,
+the `s` command will move *into* the function call
+to show what's executing inside of it.
+I view the step command as the very fine adjustment command
+to run as I get close to the problem area
+in my code.
+
+Whenever I'm done debugging my code
+and have seen all that I need to see,
+I finish with the `c` command
+to *continue* normal execution.
+
+What's great about having a unit test is that I can run the debugger
+in a very coarse way
+by liberally using the `n` command.
+As I hit the error state
+(like an exception happening),
+I can continue,
+restart the test,
+then quickly return to right before the point in time
+when the error occurs.
+At that juncture,
+I switch to the `s` command
+until I find the exact moment
+when something goes wrong.
+This process helps me avoid wasting time
+by stepping through parts of the code that don't matter.
+
+The debugger has more interesting features
+like setting breakpoints
+and checking data at different parts
+of the call stack,
+but I hope this description gives you an idea
+of how to apply a debugger
+in your workflow.
+
+Learn more about the debugger and `pdb`
+by checking out the
+{{< extlink "https://docs.python.org/3/library/pdb.html" "pdb standard libary documentation" >}}.
+
+## Use The Source, Luke
+
+At times,
+you're going to run into problems
+with code
+that is not part of your project.
+This might be code
+in the Django source
+or it might be code
+from some other library
+that you happen to use in your project.
+
+What I have found over time is
+that many less experienced developers
+will hit this situation
+and suddenly freeze.
+There's some kind of mental barrier preventing them
+from looking any further
+than the boundary
+of their own code.
+
+Here's my tip: **don't freeze!**
+
+When you wake up
+to the reality
+that it's all just code
+that people wrote,
+you cross that mental barrier
+and move into realms
+that others may fear to tread.
+The funny part is that this code is often no more special
+than anything that you might write yourself!
+In fact,
+many open source developers are fantastic programmers,
+and fantastic programmers often know how to write clear,
+maintainable,
+and well-documented code.
+
+So,
+listen to your inner "Obi-Wan"
+and use the *source*, Luke!
+
+* If you're hitting a problem with a package,
+ look at the source code on GitHub
+ (or wherever the code is hosted).
+ You can study the modules and trace through how the code would execute.
+* If that's not enough,
+ don't be afraid to step through functions
+ in other libraries
+ with your debugger
+ as you are testing out whatever problem
+ that you are trying to fix.
+* Remember `print` debugging?
+ Who says you can't do that with other software?
+ If I've got a virtual environment
+ and I need to figure out what's going on
+ with how my software interacts with a library,
+ I will sometimes add `print` statements directly
+ to the library files
+ in my virtual environment's `site-packages` directory.
+ This is a great trick!
+
+There are limitations to this approach because some packages
+will include code sections that are compiled with C.
+That kind of code is harder to peek into,
+but I've found that the majority of Python packages
+that you find on PyPI are Python-only.
+That makes most packages great candidates
+for digging into the source code.
+
+## Logging Chronology
+
+Logging is a topic that we will only scratch the surface of,
+but the subject is important
+to round out a discussion of debugging tools.
+
+A logging system provides the ability to record messages
+during the execution
+of your application.
+The easiest way to think of logging is as a permanent set
+of `print` calls
+that record information every time your functions or methods
+call the logging system.
+
+When logging is used in an application,
+you can capture what happened within your app
+even if you weren't there to observe the system
+at the moment that it happened.
+The logging system forms a chronological log
+of events
+that happen within a system.
+
+This has some big benefits,
+but comes with some tradeoffs
+to consider.
+
+* **Pro**: When you have sufficient logging,
+ you can see what is happening in the app
+ when your users are working in the app.
+* **Pro**: More advanced logging configurations
+ can include metadata that you can filter on
+ to look for specific kinds of activity.
+ This can aid your diagnostic abilities.
+* **Con**: Logging generates data.
+ While this enables the points above,
+ you now have a new challenge
+ of *managing* this new source of data.
+* **Con**: Log management is even more of a challenge
+ as an application system grows.
+ When you have multiple servers,
+ where does all that log data go?
+
+This concept of logging is not a Django-specific idea.
+In fact,
+you can find a `logging` module
+in the Python standard library
+that serves as the basis
+for logging in the Python world.
+Django builds its logging features
+on top of the `logging` module.
+
+The short version of logging
+in Django
+is that you can use logging
+by configuring the `LOGGING` settings
+in your Django settings module.
+This process is described
+in the documentation
+at {{< extlink "https://docs.djangoproject.com/en/4.1/howto/logging/" "How to configure and use logging" >}}.
+
+In a basic workflow,
+logging can be little more
+than writing lines
+of data
+to a log *file*.
+If you're running an app
+on a VPS
+like Digital Ocean
+and have control over your filesystem,
+this is a good starting option.
+If you choose that path,
+keep in mind that disk space is a limited resource.
+One way to tank your app is to fill your machine's disk
+with logging data.
+To prevent this,
+you should investigate how to use logrotate.
+{{< extlink "https://linux.die.net/man/8/logrotate" "logrotate" >}} is a command
+that can archive your log data
+on whatever frequency you desire
+and clean up old data
+to prevent your storage from filling completely.
+
+You may follow a path like I've recommended frequently
+and deploy your app on a Platform as a Service (PaaS) like Heroku.
+In that kind of environment,
+you have less control over the tools that are available
+to run your application.
+In the case of Heroku,
+the platform does not guarantee
+that your app will stay on the same machine.
+Because of that constraint,
+you can't rely on a log file
+on the machine.
+Instead,
+you would need to add an additional service
+to the application
+that will store logs
+for your later use.
+{{< extlink "https://www.papertrail.com/" "Papertrail" >}} is an example
+of a log aggregation service
+that works with Heroku.
+
+As you can see,
+logging brings some complexity to manage
+if you choose to use it.
+Logs can be a great tool
+to understand what happened
+when a user used your app,
+especially if there was an error
+that didn't raise an exception.
+For me, personally,
+I *don't* reach for logging right away.
+While logging can be useful,
+it can be an overwhelming amount of data
+that can turn debugging
+into a "needle in a haystack" problem.
+Logging is still a potentially useful tool
+for your toolbox
+as you think about how to debug your apps.
+
+## Summary
+
+{{< web >}}
+In this article,
+{{< /web >}}
+{{< book >}}
+In this chapter,
+{{< /book >}}
+we saw debugging tips, tools, and techniques
+to make you a bug crushing machine
+in Django.
+We discussed:
+
+* A systematic method for finding and fixing bugs in your app
+* How `print` is an awesome debugging tool
+ that should be used without shame
+* Debugging with a "proper" debugging tool
+ for particularly thorny problems
+* Using the source code of packages you didn't write
+ to figure out what's going on
+* Logging and the ability to see a history
+ of activity in your application
+
+{{< web >}}
+That's the end of this series!
+{{< /web >}}
+{{< book >}}
+That's the end of this book!
+{{< /book >}}
+Can you believe there are still more topics
+{{< web >}}
+that this series could cover?
+{{< /web >}}
+{{< book >}}
+that this book could cover?
+{{< /book >}}
+Django has so much available that,
+{{< web >}}
+even after twenty articles,
+{{< /web >}}
+{{< book >}}
+even after all these chapters,
+{{< /book >}}
+I've not covered everything.
+But this is the end
+of the line for me.
+
+{{< web >}}
+I knew when I started this series
+{{< /web >}}
+{{< book >}}
+I knew when I started this book
+{{< /book >}}
+that I was going to cover a huge number
+of topics.
+What I didn't know when I started this in January of 2020
+is how wacky the world would be.
+{{< web >}}
+I thought that I might produce an article every other week
+{{< /web >}}
+{{< book >}}
+I thought that I might produce a chapter every other week
+{{< /book >}}
+and be done in less than a year.
+*How wrong I was!*
+More than two years later,
+I'm writing the words
+{{< web >}}
+of this last article.
+{{< /web >}}
+{{< book >}}
+of this last chapter.
+{{< /book >}}
+
+{{< web >}}
+As I wrap up this series,
+{{< /web >}}
+{{< book >}}
+As I wrap up this book,
+{{< /book >}}
+my hope is that you,
+dear reader,
+have come to understand Django better.
+Django is a web framework written
+by people like you and me.
+That community,
+through years of collaboration,
+polished a tool
+that can bring your ideas
+to life on the web.
+
+While the framework might initially seem magical
+in all that it is able to do,
+we can see through study
+and examination
+that the code is comprehensible.
+The magic becomes less magical,
+but we can grow
+in our respect
+for those who contributed untold hours
+to making something so useful
+for others
+(for *free* in most cases!).
+
+{{< web >}}
+I would like to conclude this series
+{{< /web >}}
+{{< book >}}
+I would like to conclude this book
+{{< /book >}}
+by thanking all of you readers out there.
+Along this lengthy journey,
+so many of you have reached out
+{{< web >}}
+and told me how this series has helped you grow
+{{< /web >}}
+{{< book >}}
+and told me how this book has helped you grow
+{{< /book >}}
+as a Django developer.
+I'm hopeful that developers have used these words
+to learn and create websites
+that help their own communities.
+Knowing that I've impacted some of you
+and, by extension, the communities
+that you've helped,
+{{< web >}}
+made writing this series worth it.
+{{< /web >}}
+{{< book >}}
+made writing this book worth it.
+{{< /book >}}
+
+> Thank you for reading!
+
+{{< web >}}
+If you'd like to see more content
+like this series,
+please feel free to sign up
+for my newsletter
+where I publish all new things first.
+If you have other questions,
+you can reach me online
+on Twitter
+where I am
+{{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
+{{< /web >}}
+
diff --git a/content/understand-django/_index.pt.md b/content/understand-django/_index.pt.md
new file mode 100644
index 00000000..d268a35c
--- /dev/null
+++ b/content/understand-django/_index.pt.md
@@ -0,0 +1,48 @@
+---
+title: "Understand Django"
+aliases:
+ - /understanddjango
+ - /django
+description: >-
+ Understand Django is a book
+ on learning the Django web framework
+ to build web applications
+ with Python.
+nopaginator: true
+
+---
+
+
+
+
+
+Understand Django walks you through the Django web framework
+and explores many of the features available in Django.
+
+We start from the top at an exploration of the browser and the layers
+that make the internet work, then dig deep down through the framework
+to reveal how you can make web applications using Django.
+
+By the time you finish reading Understand Django,
+you'll have a much deeper knowledge of how web applications work
+and, specifically,
+how _Python-based_ web applications are built.
+
+You can read the book online for _free, right now,_
+and soon will be able to able to buy an e-book, physical book,
+or audio book from your favorite online stores.
+
+The content builds
+on itself
+to increase your understanding
+of how to use Django
+and explains how Django works.
+If you're new to Django,
+you can start
+from the beginning.
+The book assumes you have little or no prior knowledge
+of doing web development.
+
+Let's get started!
+
+{{< understand-django-series "none" >}}
From e9ebb0a63c5a6d61a6940da233553a841770dba2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nazar=C3=A9=20da=20Piedade?=
Date: Sat, 25 Mar 2023 10:15:35 +0000
Subject: [PATCH 02/30] docs(pt): translate `understand-django/_index.pt.md`
---
content/understand-django/_index.pt.md | 46 ++++++++------------------
1 file changed, 13 insertions(+), 33 deletions(-)
diff --git a/content/understand-django/_index.pt.md b/content/understand-django/_index.pt.md
index d268a35c..eb695c7f 100644
--- a/content/understand-django/_index.pt.md
+++ b/content/understand-django/_index.pt.md
@@ -1,13 +1,10 @@
---
-title: "Understand Django"
+title: "Entendendo a Django"
aliases:
- /understanddjango
- /django
description: >-
- Understand Django is a book
- on learning the Django web framework
- to build web applications
- with Python.
+ Entendendo a Django é um livro sobre a aprendizagem da abstração de web Django para construir aplicações de web com a Python.
nopaginator: true
---
@@ -16,33 +13,16 @@ nopaginator: true
-Understand Django walks you through the Django web framework
-and explores many of the features available in Django.
-
-We start from the top at an exploration of the browser and the layers
-that make the internet work, then dig deep down through the framework
-to reveal how you can make web applications using Django.
-
-By the time you finish reading Understand Django,
-you'll have a much deeper knowledge of how web applications work
-and, specifically,
-how _Python-based_ web applications are built.
-
-You can read the book online for _free, right now,_
-and soon will be able to able to buy an e-book, physical book,
-or audio book from your favorite online stores.
-
-The content builds
-on itself
-to increase your understanding
-of how to use Django
-and explains how Django works.
-If you're new to Django,
-you can start
-from the beginning.
-The book assumes you have little or no prior knowledge
-of doing web development.
-
-Let's get started!
+Entendendo a Django acompanha-te através da abstração de web Django e explora muitas das funcionalidades disponíveis na Django.
+
+Nós começamos desde o princípio em uma exploração do navegador e as camadas que fazem a internet funcionar, depois cavamos no fundo através da abstração para revelar como podes construir as tuas aplicações de web usando a Django.
+
+No momento que terminares a leitura do Entender a Django, terás um conhecimento mais profundo de como as aplicações de web funcionam e, especialmente, como aplicações de web _baseadas na Python_ são construídas.
+
+Tu podes ler o livro online _gratuitamente, agora mesmo_ e brevemente serás capaz de comprar um livro digital, físico, ou em áudio a partir das tuas lojas digitais favoritas.
+
+O conteúdo constrói-se sobre si mesmo para aumentar o teu entendimento de como usar a Django e explica como a Django funciona. Se fores novo para a Django, podes começar desde o princípio. O livro presume que tens um pouco ou nenhum conhecimento prévio sobre o desenvolvimento da web.
+
+Vamos começar!
{{< understand-django-series "none" >}}
From 0c7165be843033096c3cc416fe509adccc5c86f9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nazar=C3=A9=20da=20Piedade?=
Date: Tue, 11 Apr 2023 15:36:33 +0100
Subject: [PATCH 03/30] docs(pt): translate
`understand-django/browser-to-django.pt.md`
---
.../2020-01-08-browser-to-django.pt.md | 852 ++++--------------
1 file changed, 178 insertions(+), 674 deletions(-)
diff --git a/content/understand-django/2020-01-08-browser-to-django.pt.md b/content/understand-django/2020-01-08-browser-to-django.pt.md
index 2fb950a0..1037409d 100644
--- a/content/understand-django/2020-01-08-browser-to-django.pt.md
+++ b/content/understand-django/2020-01-08-browser-to-django.pt.md
@@ -1,17 +1,7 @@
---
-title: "From Browser To Django"
+title: "Do Navegador à Django"
description: >-
- Django helps you build websites
- in Python.
- How does it work?
- In this series,
- we'll explore Django
- from top to bottom
- to show you how to build
- the website you've wanted.
- We'll start
- from the beginning
- with the browser.
+ A Django ajuda-te a construir aplicações de Web em Python. Como ela funciona? Nesta série, exploraremos a Django desde a parte de cima até o fundo para mostrar-te como construir a aplicação de Web que tens desejado. Começaremos desde o princípio com o navegador.
image: img/django.png
type: post
categories:
@@ -23,343 +13,113 @@ tags:
---
-Maybe you have heard about
-{{< extlink "https://www.djangoproject.com/" "Django" >}}
-and that it can help you build websites.
-You might be new to Python,
-new to web development,
-or new to programming.
+É possível que tenhas ouvido falar sobre a {{< extlink "https://www.djangoproject.com/" "Django" >}} e que pode ajudar-te a construir aplicações de Web. Tu podes ser principiante para Python, novo para o desenvolvimento da web, ou novo para a programação.
{{< web >}}
-This new series,
-[Understand Django]({{< ref "/understand-django/_index.md" >}}),
-will show you what Django is all about.
-Throughout this series,
+Esta nova série, [Entendendo a Django]({{< ref "/understand-django/_index.pt.md" >}}), mostrar-te-á sobre o que a Django se trata. Ao longo desta série,
{{< /web >}}
{{< book >}}
-This book
-will show you what Django is all about.
-In the following chapters,
+Este livro mostrar-te-á sobre o que a Django se trata. Nos capítulos seguintes,
{{< /book >}}
-I will reveal how Django is a powerful tool
-that can unlock the potential
-of anyone interested
-in making applications
-on the internet.
-Django is used
-by companies
-like Instagram,
-Eventbrite,
-Disqus,
-and
-Udemy,
-and is also a great tool
-for individuals like you.
-
-We're going to take a high-level approach to learning Django.
-Rather than starting
-at the bottom
-with all the pieces
-of Django,
-I'll give you the big picture,
-then explore each layer in more detail
-to reveal how much Django does
-for developers
-and the power Django has
-under the hood.
-
-Let's get started
-from the very top
-of a user's internet experience:
-at the web browser.
-
-{{< understand-django-series "browser" >}}
-
-## Making A Browser Request
-
-Django is a web framework,
-but what the heck does that even mean?
-How do websites work?
-I'm not going
-to be able to walk
-through all the details,
+Eu revelarei como a Django é uma ferramenta poderosa que pode desbloquear o potencial de qualquer um interessado em criar aplicações na internet. A Django é usada por empresas como Instagram, Eventbrite, Disqus, e Udemy, e é também um excelente ferramenta para indivíduos como tu.
+
+Iremos tomar uma abordagem de alto nível para aprender a Django. No lugar de começar desde fundo com todos os pedaços da Django, dar-te-ei um grande panorama, depois explorar cada camada com mais detalhes para revelar o quanto a Django faz pelos programadores e o poder que a Django tem nos bastidores.
+
+Começaremos desde a parte mais de cima da experiência de internet do utilizador: no navegador da web.
+
+{{< understand-django-series-pt "browser" >}}
+
+## Fazendo Uma Requisição de Navegador
+
+A Django é uma abstração de web, mas o que diabo isto significa?
+Como é que as aplicações de web funcionam? Eu não serei capaz de caminhar através de todos os detalhes,
{{< web >}}
-but this post
+mas esta publicação
{{< /web >}}
{{< book >}}
-but this chapter
+mas este capítulo
{{< /book >}}
-will lay down the breadcrumbs
-to build your understanding.
-We'll look at the way your web browser requests data
-from the internet
-and the "plumbing" needed
-to make that work.
-Equipped with the key words
-and acronyms found in this chapter,
-you should be able
-to start your own research
-on these topics.
-
-The internet works
-by fulfilling a user's desire
-for sending and receiving information.
-That "information" takes many different forms.
-It might be:
-
-* Cat videos on YouTube
-* Political ramblings from social media
-* Profiles of other people on dating sites
-
-Whatever people are looking for,
-the information is transferred
-via the same mechanisms.
-In internet-speak,
-all types of information and data
-fall under the name *resource*.
-
-The way we get resources are with
-{{< extlink "https://en.wikipedia.org/wiki/URL" "Uniform Resource Locators" >}}
-or URLs,
-for short.
-You know what URLs are,
-even if you didn't know them by name.
+estabelecerá as migalhas para construir o teu entendimento. Olharemos para a maneira que o teu navegador da web requisita os dados da internet e a "canalização" necessária para fazer isto funcionar. Equipado com as palavras-chaves e acrónimos encontrados neste capítulo, serias capaz de começar a tua própria investigação sobre testes tópicos.
+
+A internet funciona compensando um desejo do utilizador por enviar e receber informação. Esta "informação" toma muitas formas diferentes. Ela pode ser:
+
+* Vídeos de gato na YouTube
+* Incoerências políticas de media social
+* Perfis de outras pessoas em locais de encontro
+Qualquer coisa que as pessoas estiverem a procura, a informação é transferida através dos mesmos mecanismos. No dizer da internet, todos os tipos de informação e dado caiem sobre o mesmo nome de *recurso*.
+
+A maneira que recebemos os recursos são com os localizadores de recursos uniformes ou URL {{< extlink "https://en.wikipedia.org/wiki/URL" "Uniform Resource Locators" >}}, para abreviar. Tu sabes o que as URLs são, ainda que não as conhecias pelo nome.
* {{< extlink "https://en.wikipedia.org/" "https://en.wikipedia.org/" >}}
* {{< extlink "https://www.djangoproject.com/" "https://www.djangoproject.com/" >}}
* {{< extlink "https://www.mattlayman.com/img/django.png" "https://www.mattlayman.com/img/django.png" >}}
-These are all examples
-of URLs.
-Often we call them web addresses
-because they're very similar to postal addresses.
-A URL is the address
-of some resource
-on the internet.
-When you hit *Enter*
-on the address bar
-of your browser,
-you're saying
-"Please browser,
-go get me this."
-In other words,
-we make a *request*
-from the browser.
-This request starts a large chain
-of events
-from your browser
-to the website at that URL
-so that the resource
-from the site
-can get to your eyeballs.
-
-What's in this chain of events?
-*Loads of things are there!*
-We'll gloss over many of the layers
-in this discussion
-because I'm guessing you aren't planning
-to get down to the level
-of how electrical signals work
-in networking cables.
-Instead,
-let's focus
-on two primary parts
-of the chain
-for now: **DNS** and **HTTP**.
-
-### Names Names Names
-
-A URL represents a resource
-that you want
-from the internet.
-How does the internet know
-where it comes from?
-That's where DNS comes in.
-DNS stands for
-{{< extlink "https://en.wikipedia.org/wiki/Domain_Name_System" "Domain Name System" >}}.
-The important word there is "Name."
-Let's return to the address analogy.
-
-In a postal address
-(at least from a US perspective),
-there is the street, city, and state.
-We might write it like:
+Estes são todos os exemplos de URLs. Frequentemente os chamamos de endereços de web porque são muito parecidos com os endereços postais. Uma URL é o endereço de algum recurso na internet. Quando pressionas *Enter* na barra de endereço do teu navegador, estás a dizer "Navegador faz o favor de ir buscar-me isto." Em outras palavras, fazemos uma *requisição* a partir do navegador. Esta requisição inicia uma grande corrente de eventos a partir do teu navegador para o local na web naquela URL para que o recurso do local possa ser visto pelos teus olhos.
+
+O que é esta corrente de eventos? *Montes de coisas estão lá!* Omitiremos muitas das camadas nesta discussão porque suponho que não planeias descer até o nível de como os sinais elétricos funcionam nos cabos de rede. No lugar disto, vamos nos focar em duas partes primárias da corrente por agora: **DNS** e **HTTP**.
+
+### Nomes Nomes Nomes
+
+Uma URL representa um recurso que queres a partir da internet. Como é que a internet sabe de onde o recurso vem? É onde o DNS entra. DNS significa {{< extlink "https://en.wikipedia.org/wiki/Domain_Name_System" "Domain Name System" >}} ou Sistema de Nome de Domínio.
+
+Em um endereço postal (pelo menos a partir de uma perspetiva dos Estados Unidos da América), existe a rua, cidade, e estado. Nós o escrevemos como:
```text
123 Main St., Springfield, IL
```
-This address goes from most narrow to most broad.
-123 Main St. is in the city
-of Springfield
-in the state of Illinois (IL).
+Este endereço vai desde o mais estreito ao mais largo. 123 Main St. é na cidade de Springfield no estado de Illinois (IL).
-Likewise,
-a URL fits into a similar format.
+Do mesmo modo, uma URL ajusta-se em um formato parecido:
```text
www.example.com
```
-The terminology is different,
-but the concept of going
-from narrow to broad
-is the same.
-Each piece between periods is a type
-of *domain*.
-Let's look at them
-in reverse order.
-
-* `com` is considered a {{< extlink "https://en.wikipedia.org/wiki/Top-level_domain" "Top Level Domain" >}}, TLD.
- TLDs are carefully managed by a special group
- called {{< extlink "https://www.icann.org/" "ICANN" >}}.
-* `example` is the domain name.
- This is the primary identity
- of a service on the internet
- as it is the specific identifier
- which a user would likely recognize.
-* `www` is considered the *subdomain*
- of a domain.
- A domain might have many of these
- like `www`, `m`, `mail`, `wiki`
- or whatever a domain owner might want to name them.
- Subdomains can also be more than one level deep
- so `a.b.example.com` is valid,
- and `a` is a subdomain of `b.example.com`
- and `b` is a subdomain of `example.com`.
-
-Domain names are *not* how computers communicate.
-The domain name is something "friendly"
-for a human.
-Networking systems are designed to work
-with numbers
-so those domain names must be translated
-into something the networking system can use.
-To do this,
-the internet uses a system
-of DNS servers
-to act as the translation layer
-between domain names
-and the numbers that computer networks use.
-A server is a special purpose computer
-designed to provide services
-for other devices called clients.
-
-Maybe you've seen these networking numbers.
-The numbers are called IP addresses,
-short for {{< extlink "https://en.wikipedia.org/wiki/Internet_Protocol" "Internet Protocol" >}} addresses.
-Common examples would include:
-
-* `127.0.0.1` as the address that your computer has
- *for itself*
- on its internal network.
-* `192.168.0.1` as a default address
- that a home router might use.
-
-The IP address examples above are special
-because those addresses are in specially designated {{< extlink "https://en.wikipedia.org/wiki/Subnetwork" "subnetworks" >}},
-but we'll set that tangent aside.
-You can delve deeper
-on that topic
-on your own
-if you would like.
-
-Private networks have IP addresses
-like the two examples I listed above.
-Machines on public networks also have IP addresses.
-For instance, `172.253.115.105` is an IP address
-for `www.google.com`
-at the time of this writing.
-
-If you'd like to figure out the IP address
-of a domain name,
-you can install a popular tool named `dig`.
-I found Google's IP address by running this command:
+A terminologia é diferente, mas o conceito de ir do mais estreito ao largo é o meso. Cada pedaço entre os pontos finais é um tipo de *domínio*. Vamos olhá-los em ordem inversa.
+
+* `com` é considerado um {{< extlink "https://en.wikipedia.org/wiki/Top-level_domain" "Top Level Domain" >}}, TDL ou Domínio de Alto Nível. Os domínios de alto nível são cuidadosamente administrados por um grupo especial chamado {{< extlink "https://www.icann.org/" "ICANN" >}}.
+* `example` é o nome de domínio. Isto é a identidade primária de um serviço na internet embora seja o identificador específico que um utilizador provavelmente reconheceria.
+* `www` é considerado o *subdomínio* de um domínio. Um domínio pode ter muitos destes como `www`, `m`, `mail`, `wiki` ou tudo aquilo que um proprietário de domínio quiser nomeia-los. Os subdomínios também podem ser mais de um nível de profundidade assim `a.b.example.com` é válido, e `a` é um subdomínio de `b.example.com` e `b` é um subdomínio de `example.com`.
+
+Os nomes de domínios *não* são como os computadores comunicam. O nome de domínio é algo "amigável" para um humano. Os sistemas de rede são desenhados para funcionar com números assim estes nomes de domínio devem ser traduzidos em algo que o sistema de rede possa usar. Para fazer isto, a internet usa um sistema de servidores de DNS para atuar como camada de tradução entre os nomes de domínio e os números que as redes de computadores usam. Um servidor é um computador de propósito especial desenhado para fornecer serviços para outros dispositivos chamados de clientes.
+
+Talvez tens visto estes números de rede. Os números são chamados de endereços de IP, abreviação para endereços de {{< extlink "https://en.wikipedia.org/wiki/Internet_Protocol" "Internet Protocol" >}} ou Protocolos de Internet. Os exemplos comuns incluiriam:
+
+* `127.0.0.1` como o endereço que o teu computador tem *para si* na sua rede interna.
+* `192.168.0.1` como um endereço padrão que um roteador de cada pode usar.
+
+Os exemplos de endereço de IP acima são especiais porque estes endereços são especialmente denominados {{< extlink "https://en.wikipedia.org/wiki/Subnetwork" "subnetworks" >}} ou subredes, mas definiremos esta tangente à parte. Tu podes vasculhar mais a fundo neste tópico por conta própria se gostarias.
+
+As redes privadas têm endereços de IP como os dois exemplos que listei acima. As máquinas em redes públicas também têm endereços de IP. Por exemplo, `172.253.115.105` é um endereço de IP para `www.google.com` no momento desta escrita.
+
+Se gostarias de compreender o endereço de IP de um nome de domínio, podes instalar uma ferramenta popular chamada `dig`. Eu descobri o endereço de IP da Google executando este comando:
```bash
dig www.google.com
```
-The system takes domain names
-and keeps a distributed routing table
-of names to IP addresses
-across the collection
-of DNS servers.
-**Wait, what?**
-
-DNS servers stack up into a gigantic hierarchy.
-When your browser makes a request,
-it asks the closest DNS server
-to your machine
-for the IP address
-of the domain name you requested.
-The DNS server keeps a lookup table
-of domain names to IP addresses
-for a period of time.
-If the domain name isn't in the table,
-it can ask another DNS server
-in a chain
-that will continue to look
-for the domain's IP address.
-This leads to a couple of outcomes:
-
-* If none of the servers can find the domain,
- the browser gives up
- and shows you a message like
- "Hmm. We’re having trouble finding that site."
- (from Firefox's Server Not Found page).
-* If the browser gets the IP address
- from the DNS server,
- it can proceed with the request.
-
-The hierarchy is gigantic,
-but it is wide, not deep.
-In other words,
-there are many machines
-that participate in DNS (like your home router),
-but the number of links in the chain
-to make a request from your computer up
-to the root servers in the system
-is relatively small.
-
-This is simplified
-to exclude some
-of the warty corners
-of DNS.
-The Wikipedia page
-that I linked at the start
-of this section covers DNS
-in much greater detail
-if you're interested
-in learning more.
-
-### What Are We Sending?
-
-The other vital piece that we need to explore is HTTP,
-or the {{< extlink "https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol" "Hypertext Transfer Protocol" >}}.
-This part of internet communication describes
-how content transfers
-between browsers
-and servers,
-or,
-more generally,
-between any computers
-that use the protocol.
-
-The protocol uses a standard format
-and a set of commands
-to communicate.
-A few of the common commands are:
-
-* `GET` - Fetch an existing resource
-* `POST` - Create or update a resource
-* `DELETE` - Delete a resource
-* `PUT` - Update a resource
-
-An HTTP request is like sending a text file over the network.
-If you visit my website
-at `https://www.mattlayman.com/about/`,
-your browser will send a request like:
+O sistema pega os nomes de domínio e preserva uma tabela de roteamento distribuído dos nomes para o endereço de IP através da coleção de servidores de DNS. **Espera, o quê?**
+
+Os servidores de DNS empilham-se em uma hierarquia gigantesca. Quando o teu navegador faz uma requisição, ele pedi o servidor de DNS mais próximo da tua máquina para o endereço de IP do nome de domínio que requisitaste. O servidor de DNS mantém uma tabela de consulta de nomes de domínio para os endereços de IP por um período de tempo. Se o nome de domínio não estiver na tabela, ele pode pedir um outro servidor de DNS numa cadeia que continuará a procurar pelo endereço de IP do domínio. Isto conduz à um alguns resultados:
+
+* Se nenhum dos servidores puder encontrar o domínio, o navegador desiste e mostra-te uma mensagem como "Hum. Estamos tendo problemas em encontrar este local." (a partir da página Não Encontrada do servidor do Firefox).
+* Se o navegador receber o endereço de IP a partir do servidor de DNS, ele pode prosseguir com a requisição.
+
+A hierarquia é gigantesca, mas é extensa, não profunda. Em outras palavras, existem muitas máquinas que participam no DNS (como o teu roteador de casa), mas o número de ligações na cadeia para fazer uma requisição desde o teu computador aos servidores de raiz no sistema é relativamente pequeno.
+
+Isto é simplificado para excluir algumas das partes defeituosas do DNS. A página da Wikipedia que liguei no princípio desta seção cobre o DNS em detalhes ainda maiores se estiveres interessado em aprender mais.
+
+### O Que Nós Estamos a Enviar?
+
+O outro pedaço essencial que precisamos de explorar é o HTTP, ou o {{< extlink "https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol" "Hypertext Transfer Protocol" >}} ou Protocolo de Transferência de Hipertexto. Esta parte da comunicação de internet descreve como o conteúdo é transferido entre navegadores e servidores. O protocolo usa um formato padrão e um conjunto de comandos para comunicar. Alguns dos comandos comuns são:
+
+* `GET` - Pedi um recurso existente
+* `POST` - Cria ou atualiza um recurso
+* `DELETE` - Elimina um recurso
+* `PUT` - Atualiza um recurso
+
+Uma requisição de HTTP é como enviar um ficheiro de texto sobre a rede. Se visitares a minha página em `https://www.mattlayman.com/about/`, o teu navegador enviará uma requisição como:
```http
GET /about/ HTTP/1.1
@@ -367,358 +127,156 @@ Host: www.mattlayman.com
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
```
-There are other parts that I've omitted,
-but this gets us started.
-The top line provides the command,
-the path to a particular resource on the site
-(i.e., `/about/`),
-and a version of the protocol to use.
-
-After the first line are a list of *headers*.
-Headers are extra data
-that tell the server more about the request.
-The `Host` header is required
-because it names the website to retrieve
-(more than one website can exist on the same IP address),
-but any other header is optional.
-
-In the example,
-I also showed the `Accept` header.
-This header tells the server
-what kind of content the browser can receive
-as a response.
-There are other headers
-that can tell a server what else it should "know."
-These headers can:
-
-* Indicate what kind of browser is making the request
- (this is the `User-Agent` header).
-* Tell when the resource was requested previously
- to determine if a new version should be returned
- (the `Last-Modified` header).
-* Declare that the browser can receive compressed data
- which it can decompress after receiving
- to save on bandwidth
- (the `Accept-Encoding` header).
-
-Most of the headers are handled automatically
-by browsers and servers,
-but we will see instances
-where we want to use these headers ourselves
-so it's good to know they exist.
-
-## Serving A Response
-
-It's time to discuss Django!
-We now have a rough idea
-of what browsers do.
-A browser sends an HTTP request
-to a URL
-which is resolved
-by the DNS system.
-That request arrives at a server
-that is connected to the IP address
-of the domain name.
-Django lives on such a server
-and is responsible
-for answering requests
-with an HTTP *response*.
-
-The response is what the browser user wanted.
-Responses can be images, web pages, videos,
-or whatever formats a browser can handle.
-
-Before Django can handle a request,
-there is one more layer
-to traverse:
-the Python web server.
-
-### Where HTTP Meets Python
-
-A web server is the software
-on a machine designed
-to handle the incoming HTTP requests.
-Sometimes this terminology can be confusing
-because people may also apply the name "web server"
-to an entire *machine*
-that is serving web traffic.
-In this instance,
-I'm referring
-to the actual program listening and responding
-to web requests.
-
-A Python web framework
-like Django
-runs with a web server.
-The web server's role is to translate the raw HTTP request
-into a format
-that the framework understands.
-In the Python world,
-there is a specific format used
-so that any web server
-can talk to any Python web framework.
-That format is the {{< extlink "https://wsgi.readthedocs.io/en/latest/what.html" "Web Server Gateway Interface" >}},
-or WSGI
-(which is often pronounced "wiz-gee").
+Existem outras partes que tenho omitido, mas esta permite-nos começar. A linha acima fornece o comando, o caminho para um recurso particular sobre a página (por exemplo, `/about/`, e uma versão do protocolo para usares).
+
+Depois da primeira linha existe uma lista de *cabeçalhos*. Os cabeçalhos são dados adicionais que dizem ao servidor mais sobre a requisição. O cabeçalho `Host` é obrigatório porque nomeia a página à extrair (mais de uma página pode existir no mesmo endereço de IP), mas qualquer outro cabeçalho é opcional.
+
+No exemplo, também mostrei o cabeçalho `Accept`. Este cabeçalho diz ao servidor qual é o tipo de conteúdo o navegador pode receber como resposta. Existem outros cabeçalhos que podem dizer à um servidor que mais ele deveria "saber". Estes cabeçalhos podem:
+
+* Indicar qual é o tipo de navegador que está fazer o requisição (isto é o cabeçalho `User-Agent`).
+* Dizer quando o recurso foi requisitado anteriormente para determinar se uma nova versão deveria ser retornada (o cabeçalho `Last-Modified`).
+* Declarar que o navegador pode receber dados compactados os quais pode descompactar depois de receber para guardar sobre a largura de banda (o cabeçalho `Accept-Encoding`).
+
+A maioria dos cabeçalhos são manipulados automaticamente pelos navegadores e servidores, mas veremos exemplos onde queremos usar estes cabeçalhos nós mesmos então é bom saber que existem.
+
+## Servindo Uma Resposta
+
+É hora de falar de Django! Agora temos uma ideia grosseira do que os navegadores fazem. Um navegador envia uma requisição de HTTP para uma URL que é resolvida pelo sistema de DNS. Esta requisição chega em um servidor que está conectado ao endereço de IP do nome de domínio. A Django mora em tal servidor e é responsável por responder as requisições com uma *resposta* de HTTP.
+
+A resposta é o que o utilizador do navegador queria. As respostas podem ser imagens, páginas de web, vídeos, ou quaisquer formatos que um navegador puder manipular.
+
+Antes da Django puder lidar com uma requisição, existe mais uma camada à atravessar: o servidor de web da Python.
+
+### Onde a HTTP Encontra a Python
+
+Um servidor de web é um software sobre uma máquina desenhada para lidar com as requisições de HTTP do exterior. Algumas vezes esta terminologia pode ser confusa porque as pessoas também podem aplicar o nome "servidor de web" à uma *máquina* inteira que está a servir o tráfego de web. Neste exemplo, estou a referir-me a um programa real ouvindo e respondendo às requisições de web.
+
+Uma abstração de web de Python como Django executa com um servidor de web. O papel do servidor de web é traduzir a requisição de HTTP crua em um formato que a abstração entenda. No mundo de Python, existe um formato específico usado para que qualquer servidor de web possa falar com qualquer abstração de web de Python. Este formato é o {{< extlink "https://wsgi.readthedocs.io/en/latest/what.html" "Web Server Gateway Interface" >}} ou Interface de Portal de Servidor de Web, WSGI em Inglês (o qual é frequentemente pronunciado como "wiz-gee").
{{< web >}}
{{< figure src="/img/2020/wsgi.jpg" caption="Web Server Gateway Interface" >}}
{{< /web >}}
-WSGI enables common web servers
-like
-{{< extlink "https://gunicorn.org/" "Gunicorn" >}},
-{{< extlink "https://uwsgi-docs.readthedocs.io/en/latest/" "uWSGI" >}},
-or {{< extlink "https://modwsgi.readthedocs.io/en/develop/" "mod_wsgi" >}}
-to communicate
-with common Python web frameworks
-like Django,
-{{< extlink "https://palletsprojects.com/p/flask/" "Flask" >}},
-or {{< extlink "https://trypyramid.com/" "Pyramid" >}}.
-If you really want to nerd out,
-you can explore all the details
-of that format
-in {{< extlink "https://www.python.org/dev/peps/pep-3333/" "PEP 3333" >}}.
-
-### Django's Job
-
-Once the web server sends a request,
-Django needs to return a *response*.
-Your role as a Django developer is
-to define the resources
-that will be available
-from the server.
-That means you must:
-
-* Describe the set of URLs
- that Django will react to.
-* Write the code
- that powers those URLs
- and returns the response.
-
-There is a ton
-to unpack in those two statements
-so we will explore individual topics
+A WSGI ativa servidores de web comum como {{< extlink "https://gunicorn.org/" "Gunicorn" >}}, {{< extlink "https://uwsgi-docs.readthedocs.io/en/latest/" "uWSGI" >}}, ou {{< extlink "https://modwsgi.readthedocs.io/en/develop/" "mod_wsgi" >}} para comunicar com abstrações de web de Python como a Django, {{< extlink "https://palletsprojects.com/p/flask/" "Flask" >}}, ou {{< extlink "https://trypyramid.com/" "Pyramid" >}}. Se realmente quiseres saber mais a fundo, podes explorar todos os detalhes deste formato no {{< extlink "https://www.python.org/dev/peps/pep-3333/" "PEP 3333" >}}.
+
+### A Tarefa da Django
+
+Assim que o servidor de web enviar uma requisição, a Django precisa de retornar uma *resposta*. O teu papel como um programador de Django é definir os recursos que estarão disponíveis a partir do servidor. Isto significa que deves:
+
+* Descrever o conjunto de URLs para os quais a Django reagirá.
+* Escrever o código que alimenta estas URLs e retornam a resposta.
+
+Existe uma tonelada para desempacotar nestas declarações assim exploraremos os tópicos individualmente
{{< web >}}
-in future articles.
+nos futuros artigos.
{{< /web >}}
{{< book >}}
-in future chapters.
+nos futuros capítulos.
{{< /book >}}
-By now,
-I hope you have an idea
-of how a request gets
-from your browser
-to a machine running Django.
-
+Por agora, espero que tenhas uma ideia de como uma requisição sai do teu navegador para um máquina executando a Django.
{{< web >}}
-{{< figure src="/img/2020/request-response.jpg" caption="Life of a browser request" >}}
+{{< figure src="/img/2020/request-response.jpg" caption="Vida de uma requisição de navegador" >}}
{{< /web >}}
{{< web >}}
-This article is relatively free
+Este artigo está relativamente livre
{{< /web >}}
{{< book >}}
-This chapter is relatively free
+Este capítulo está relativamente livre
{{< /book >}}
-of code examples,
-and for good reason.
-There are already enough concepts
-to wrestle with
-and I didn't want to add code complexity
-on top of it.
-Writing that code will be the focus
+de exemplos de código, e por boa razão. Já existem conceitos suficientes para debater-se e não queria adicionar complexidade de código sobre isto. Escrever este código será o foco
{{< web >}}
-of this article series
+desta séries de artigos
{{< /web >}}
{{< book >}}
-of this book
+deste livro
{{< /book >}}
-so we can answer questions like:
-
-* How do we build web pages
- and give everything a common look and feel?
-* How can users interact
- with an application
- and send data
- that the app can react to?
-* How does Django store and retrieve data
- to make sites dynamic?
-* Who can access the application
- and how is that access controlled?
-* What security do we need to add
- to ensure that our users' information is safe and private?
-
-Django has answers for all these things
-and way more.
-The Django philosophy is to include all the required pieces
-to make a full web application
-for the internet.
-This "batteries-included" philosophy is what makes Django so powerful.
-The same philosophy can also make Django seem overwhelming.
+então poderemos responder questões como:
+
+* Como é que construímos páginas de web e damos a tudo uma aparência comum?
+* Como é que os utilizadores podem interagir com uma aplicação e enviar dados com os quais a aplicação possa reagir?
+* Como é que a Django armazena e recupera dos dados para tornar os páginas dinâmicas?
+* Quem pode acessar a aplicação e como é que este acesso é controlado?
+* Que segurança precisamos de adicionar para garantir que a informação dos nossos utilizadores está segura e privada?
+
+A Django tem respostas para todas estas questões e muito mais. A filosofia da Django é de incluir todos os pedaços necessários para fabricar uma aplicação completa de web para a internet. Esta filosofia de "baterias inclusas" é o que torna a Django tão poderosa. A mesma filosofia também pode fazer a Django parecer avassaladora.
{{< web >}}
-My goal in this series is to introduce piece after piece
+O meu objetivo nesta série é introduzir pedaço após pedaço
{{< /web >}}
{{< book >}}
-My goal in this book is to introduce piece after piece
+O meu objetivo neste livro é introduzir pedaço após pedaço
{{< /book >}}
-to build your understanding of Django
-so you can get productive and get going
-on your own web application.
+para construir o teu entendimento de Django para que assim possas ser produtivo e possas construir a tua própria aplicação de web.
{{< web >}}
-In the next article,
-our focus is going to be
-on those URLs
-that our application
-will respond to.
-We will see:
-
-* how to declare the URLs.
-* how to group sets of related URLs.
-* how to extract information from URLs
- that can be used by the code that returns responses.
-
-If you'd like to follow along
-with the series,
-please feel free to sign up
-for my newsletter
-where I announce all of my new content.
-If you have other questions,
-you can reach me online
-on Twitter
-where I am
-{{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
+No próximo artigo, o nosso foco será nestas URLs para as quais a nossa aplicação responderá. Nós veremos:
+
+* Como declarar as URLs.
+* Como agrupar conjuntos de URLs relacionadas.
+* Como extrair informação das URLs que possa ser usada pelo código que retorna respostas.
+
+Se gostarias de seguir com a série, podes inscrever-te no meu boletim informativo onde anuncio todos os novos conteúdo. Se tiveres outras questões, podes contactar-me online na Twitter onde sou o {{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
{{< /web >}}
-Finally,
-there is one more bonus topic...
+Finalmente, existe mais um tópico de bónus...
-## Getting Django Set Up
+## Começar a Configurar a Django
{{< web >}}
-In the series,
+Na série,
{{< /web >}}
{{< book >}}
-In the book,
+No livro,
{{< /book >}}
-we'll be looking at plenty of code examples,
-but we won't be setting up Django from scratch each time.
-The following setup instructions will help you get started
-with each future example.
-
-> The goal of this section is not meant to be an authoritative description
-of how to set up your Python environment.
-I am assuming that you have some knowledge of how to run Python code.
-If you need a more descriptive guide,
-I'd suggest Michael Kennedy's
-[Installing Python 3](https://training.talkpython.fm/installing-python) article
-and Real Python's
-[primer on virtual environments](https://realpython.com/python-virtual-environments-a-primer/).
-These article go into the discussion of setup far more
-than I'm doing justice here.
-
-We're going to use a terminal to run commands.
-Windows, macOS, and Linux are all a bit different.
-I'm showing macOS here
-because that's what I run.
-The dollar sign (`$`) is the traditional starting character
-for a bash terminal
-so when I list commands,
-don't type that character.
-I'll try to give pointers
-and highlight differences when I can.
-
-We need a place to put our work.
+veremos muitos exemplos de código, mas não configuraremos a Django desde zero repetidamente. As seguintes instruções de configuração ajudar-te-ão a começar com cada exemplo futuro.
+
+> O objetivo desta seção não é ser uma descrição fidedigna de como configurar o teu ambiente de Python. Eu estou a assumir que tens algum conhecimento de como executar código de Python. Se precisares de um guia mais descritivo, eu sugeriria o artigo [Instalando a Python 3](https://training.talkpython.fm/installing-python) do Michael Kennedy e o [Compêndio sobre Ambientes Virtuais](https://realpython.com/python-virtual-environments-a-primer/) da Real Python. Estes artigos entram na discussão da configuração muito mais do que estou a fazer justiça aqui.
+
+Usaremos um terminal para executar os comandos. Windows, MacOS, e Linux são todos um pouco diferentes. Estou a mostrar o terminal de MacOS porque é o que uso. O sinal de dólar (`$`) é o carácter inicial tradicional para um terminal de bash então quando eu listar os comandos, não digite este carácter. Tentarei dar indicações e realçar as diferenças quando puder.
+
+Nós precisamos de um lugar para colocar o nosso trabalho.
{{< web >}}
-Since this series is called "Understand Django,"
+Já que esta séria é chamada de "Entendendo a Django"
{{< /web >}}
{{< book >}}
-Since this book is called "Understand Django,"
+Já que este livro é chamado de "Entendendo a Django,"
{{< /book >}}
-I'm going to use that name.
-You can name your project differently if you prefer.
+Usarei este nome, mas em Inglês. Tu podes nomear o teu projeto de maneira diferente se preferires:
```bash
$ mkdir understand-django
$ cd understand-django
```
-Next,
-we install Django
-into a virtual environment
-so we keep our project dependencies separate
-from the rest
-of the installed Python packages
-on our machine.
-Having this separation
-from other installed packages
-is a good way to prevent conflicts
-with other Python projects
-that you may be running
-on your computer.
+A seguir, instalaremos a Django em um ambiente virtual assim mantemos as dependências do nosso projeto separadas do resto dos pacotes de Python instalados na nossa máquina. Ter esta separação de outros pacotes instalados é uma boa maneira de evitar conflitos com outros projetos de Python que podes estar a executar no teu computador.
```bash
$ python3 -m venv venv
$ source venv/bin/activate
```
-This may change your terminal prompt
-so that it will now start with `(venv)`
-to tell you that the virtual environment is in use.
-Other operating systems activate the virtual environment differently.
-Check the {{< extlink "https://docs.python.org/3/library/venv.html" "venv module documentation" >}}
-for more information
-on your operating system.
+Isto pode mudar o pronto do teu terminal para que agora comece com `(venv)` para dizer-te que o ambiente virtual está em uso. Outros sistemas operativos ativam o ambiente virtual de maneira diferente. Consulte a {{< extlink "https://docs.python.org/3/library/venv.html" "documentação do módulo venv" >}} por mais informações sobre o teu sistema operativo.
-Now you can install Django,
-and the Django framework code will be added
-to the virtual environment.
+Agora podes instalar a Django, e o código da abstração Django será adicionado ao ambiente virtual:
```bash
(venv) $ pip install Django
```
-Django includes some tools
-which we can use
-to get a project started quickly.
-We'll run a single command
-to get it going.
+A Django inclui algumas ferramentas que podemos usar começar um projeto rapidamente. Executaremos um único comando gerar o projeto:
```bash
(venv) $ django-admin startproject project .
```
-This commands says
-"start a project
-*named* 'project'
-in the current directory (`.`)."
-The choice of "project" as the name is intentional.
-`startproject` will create a directory
-named `project` that will contain various files
-that you'll use to configure your entire web app.
-You can name your project whatever you like,
-but I find that using the generic name makes my life easier
-as I switch between different Django web apps.
-I always know where my project related files reside.
-After that command is finished,
-you should have some files
-and a layout that looks like:
+Este comando diz "começar um projeto *nomeado* 'project'" no diretório atual (`.`)." A escolha de "project" como nome é intencional. `startproject` criará um diretório nomeado `project` que conterá vários ficheiros que usarás para configurar o teu aplicação de web inteira. Tu podes nomear o teu projeto como queiras, mas considero que usar o nome genérico torna a minha vida muito mais fácil visto que alterno entre diferentes aplicações de web em Django. Eu sempre sei onde os ficheiros relacionados ao meu projeto residem. Depois daquele comando estiver terminado, deves ter alguns ficheiros e uma estrutura que parece-se com:
```bash
(venv) $ ls
manage.py project venv
```
-Notice that,
-in addition to the `project` directory,
-Django created a `manage.py` file.
-This file is a script that will help you interact
-with Django.
-You'll learn a lot more about `manage.py`
-as we get farther along.
-To check if the basics are working,
-try:
+Repara que, além do diretório `project`, a Django criou um ficheiro `manage.py`. Este ficheiro é um programa que ajudar-te-á a interagir com a Django. Aprenderás mais sobre `manage.py` a medida que formos avançando. Para verificar se as bases estão a funcionar, experimente:
```bash
(venv) $ python manage.py runserver
@@ -727,9 +285,7 @@ Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
```
-When you start the web server,
-you will likely see a message
-like:
+Quando inicializares o servidor de web, provavelmente verás uma mensagem como:
```text
You have ## unapplied migration(s).
@@ -738,61 +294,21 @@ until you apply the migrations for app(s):
```
-We'll explore the migrations topic later,
-so don't worry about that message for now.
-
-If you copy and paste that URL
-(i.e., `http://127.0.0.1:8000/`)
-into your browser,
-you should see a welcoming start page!
-Also,
-if you look back at your terminal,
-you'll find `"GET / HTTP/1.1"`.
-This message is showing that Django responded
-to an HTTP request.
-Neat!
-
-The other thing that we need is an "app."
-This is (perhaps confusingly) the name
-of a Django component
-in a project.
-What you need to remember is
-that a Django project *contains* one or more apps.
-Apps will hold most
-of your code
-that you need to write
-when working with Django.
-
-After you have quit the server,
-you can create an app to work with:
+Exploraremos o tópico de migrações depois, então não te preocupes com aquela mensagem por agora.
+
+Se copiares e colares aquela URL (por exemplo, `http://127.0.0.1:8000/`) para o teu navegador, deves ver uma página inicial de boas-vindas! Além disto, se olhares para trás no teu terminal, encontrarás `"GET / HTTP/1.1"`. Esta mensagem está a indicar que a Django respondeu à uma requisição de HTTP. Espetacular!
+
+A outra coisa que precisamos é de uma aplicação ou "app". Isto é (talvez confusamente) o nome de um componente de Django num projeto. O que precisas de lembrar é que um projeto de Django *contém* um ou mais aplicações. As aplicações segurarão a maior parte do teu código que precisas de escrever quando trabalhas com a Django.
+
+Depois tens que parar o servidor, podes criar uma aplicação com a qual trabalhar através do seguinte comando:
```bash
(venv) $ python manage.py startapp application
```
-This will generate another set of files
-that follow the standard structure
-of a Django application component
-inside a directory called `application`.
-This example uses a boring name,
-but,
-unlike `project`,
-you should pick a name
-that makes sense for your web app
-(e.g., `movies` would be a good name
-for a web app that is about movies).
-All of these files will be discussed
-in detail in a future topic.
-
-Finally,
-we must hook that app
-into Django's project settings.
-The project settings allow you to configure Django
-to suit your needs.
-Open up `project/settings.py`,
-find `INSTALLED_APPS`
-and append to the list
-so it looks like:
+Isto gerará um outro conjunto de ficheiros que segue a estrutura padrão de um componente de aplicação de Django dentro de um diretório chamado `application`. Este exemplo usa um nome aborrecido, mas ao contrário de `project`, deverias escolher um nome que faça sentido para a tua aplicação de web (por exemplo, `movies` seria um bom nome para uma aplicação de web que é sobre cinema). Todos estes ficheiros serão discutidos em detalhes num tópico futuro.
+
+Finalmente, devemos prender esta aplicação nas definições de projeto da Django. As definições de projeto permitem-te configurar a Django para ajustar-se às tuas necessidades. Abra `project/settings.py`, encontre `INSTALLED_APPS` e anexe o nome da tua aplicação à lista assim ela parece-se com isto:
```python
INSTALLED_APPS = [
@@ -806,26 +322,14 @@ INSTALLED_APPS = [
]
```
-That's as far as we need to go
-to get started
-with our code examples
+Isto é o quanto precisamos para avançar para começar com os nossos exemplos de código
{{< web >}}
-in the next article.
+no próximo artigo.
{{< /web >}}
{{< book >}}
-in the next chapter.
+no próximo capítulo.
{{< /book >}}
-`application` will be our reference app.
-The code in future topics is not a tutorial,
-but I will use `application` on occasion
-to orient you to where you would find files
-in your own Django web app.
-We have a Django project
-that can run locally
-for testing
-and is configured
-with its first app.
+`application` será a nossa aplicação de referência. O código nos tópicos futuros não é um passo-a-passo, mas usarei a `application` ocasionalmente para orientar-te onde encontrarias os ficheiros na tua própria aplicação de web de Django. Nós temos um projeto de Django que podemos executar localmente para testagem e está configurado com a sua primeira aplicação.
{{< web >}}
-See you soon
-to talk about making URLs and resources!
+Até à próxima para falarmos sobre a criação de URLS e recursos!
{{< /web >}}
From 2517934974ded0c15accfc5f52eafd85951a939e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nazar=C3=A9=20da=20Piedade?=
Date: Fri, 5 May 2023 08:11:27 +0100
Subject: [PATCH 04/30] chore: format
`understand-django/2020-03-03-views-on-views.pt.md`
---
.../2020-03-03-views-on-views.pt.md | 667 +++---------------
1 file changed, 113 insertions(+), 554 deletions(-)
diff --git a/content/understand-django/2020-03-03-views-on-views.pt.md b/content/understand-django/2020-03-03-views-on-views.pt.md
index 0d21eb90..9adc956b 100644
--- a/content/understand-django/2020-03-03-views-on-views.pt.md
+++ b/content/understand-django/2020-03-03-views-on-views.pt.md
@@ -1,15 +1,7 @@
---
title: "Views On Views"
description: >-
- Django URLs expect to send a response
- back to a user.
- Where does that response come from?
- A Django view!
- This article looks
- into the fundamentals
- of views
- and how to use them
- in your project.
+ Django URLs expect to send a response back to a user. Where does that response come from? A Django view! This article looks into the fundamentals of views and how to use them in your project.
image: img/django.png
type: post
categories:
@@ -23,75 +15,31 @@ tags:
---
{{< web >}}
-In the previous
-[Understand Django]({{< ref "/understand-django/_index.md" >}})
-article,
-I covered URLs
-and the variety
-of tools
-that Django gives us
-to describe the outside interface
-to the internet
-for your project.
-In this article,
+In the previous [Understand Django]({{< ref "/understand-django/_index.md" >}}) article, I covered URLs and the variety of tools that Django gives us to describe the outside interface to the internet for your project. In this article,
{{< /web >}}
{{< book >}}
-Now that we have a grasp
-on URLs in Django,
+Now that we have a grasp on URLs in Django,
{{< /book >}}
-we'll examine the core building block
-that makes those URLs work:
-the Django view.
+we'll examine the core building block that makes those URLs work: the Django view.
{{< understand-django-series "views" >}}
## What Is A View?
-A view is a chunk of code
-that receives an HTTP request
-and returns an HTTP response.
-Views are where you use Django's core functionality:
-to respond to requests
-made to an application
-on the internet.
-
-You might notice
-that I'm a bit vague
-about "chunk of code."
-That was deliberate.
-The reason is that views come
-in multiple forms.
-To say views are *functions*
-would be part
-of the story.
-Later chapters in that story cover
-how they can also be implemented in *classes*.
-
-Even if I attempted
-to call views *callables,*
-I still would not portray them accurately
-because of the ways
-that certain types of views
-get plugged into a Django app.
-For instance,
-a view based on a class will *produce* a callable
-as we'll see in a later section.
-
-Let's start with functions
-since I think they are the gentlest introduction
-to views.
+A view is a chunk of code that receives an HTTP request and returns an HTTP response. Views are where you use Django's core functionality: to respond to requests made to an application on the internet.
+
+You might notice that I'm a bit vague about "chunk of code." That was deliberate. The reason is that views come in multiple forms. To say views are *functions* would be part of the story. Later chapters in that story cover how they can also be implemented in *classes*.
+
+Even if I attempted to call views *callables,* I still would not portray them accurately because of the ways that certain types of views get plugged into a Django app. For instance, a view based on a class will *produce* a callable as we'll see in a later section.
+
+Let's start with functions since I think they are the gentlest introduction to views.
## Function Views
-A function view is precisely that, a function.
-The function takes an `HttpRequest` instance
-as input
-and returns an `HttpResponse`
-(or one of its many subclasses)
+A function view is precisely that, a function. The function takes an `HttpRequest` instance as input and returns an `HttpResponse` (or one of its many subclasses)
as output.
-The classic "Hello World" example
-would look like what is listed below.
+The classic "Hello World" example would look like what is listed below:
```python
# application/views.py
@@ -101,63 +49,24 @@ def hello_world(request):
return HttpResponse('Hello World')
```
-Adding the `hello_world` view
-to a URL configuration
-which we learned about
+Adding the `hello_world` view to a URL configuration which we learned about
{{< web >}}
in the last article,
{{< /web >}}
{{< book >}}
in the last chapter,
{{< /book >}}
-you could visit a browser
-at the URL
-and find the text "Hello World"
-on your browser page.
-
-Maybe you don't find that very exciting,
-but I do,
-and I think you should!
-The framework did so much work for us,
-and *our* job is to write a mere couple of lines
-of Python.
-When plugged into a web server
-on the internet,
-your greeting can reach anyone
-with access to the net.
-That's staggering
-and is worth reflecting on.
-
-Django does most of the heavy lifting
-for us.
-The raw HTTP request fits neatly
-into the `HttpRequest` class.
-Our example view doesn't use that information,
-but it's accessible
-if we need it.
-Likewise,
-we're not using much
-of `HttpResponse`.
-Still,
-it's doing all the work
-to ensure it appears
-on a user's browser
-and delivers our message.
-
-To see what we can do with views,
-let's look closely
-at `HttpRequest` and `HttpResponse`
-to get a glimpse
-at what's going on.
+you could visit a browser at the URL and find the text "Hello World" on your browser page.
+
+Maybe you don't find that very exciting, but I do, and I think you should! The framework did so much work for us, and *our* job is to write a mere couple of lines of Python. When plugged into a web server on the internet, your greeting can reach anyone with access to the net. That's staggering and is worth reflecting on.
+
+Django does most of the heavy lifting for us. The raw HTTP request fits neatly into the `HttpRequest` class. Our example view doesn't use that information, but it's accessible if we need it. Likewise, we're not using much of `HttpResponse`. Still, it's doing all the work to ensure it appears on a user's browser and delivers our message.
+
+To see what we can do with views, let's look closely at `HttpRequest` and `HttpResponse` to get a glimpse at what's going on.
## HttpRequest
-`HttpRequest` is a Python class.
-Instances of this class represent an HTTP request.
-HTTP is the transfer protocol
-that the internet uses to exchange information.
-A request can be in a variety of formats,
-but a standard request might look like:
+`HttpRequest` is a Python class. Instances of this class represent an HTTP request. HTTP is the transfer protocol that the internet uses to exchange information. A request can be in a variety of formats, but a standard request might look like:
```http
POST /courses/0371addf-88f7-49e4-ac4d-3d50bb39c33a/edit/ HTTP/1.1
@@ -181,115 +90,41 @@ name=Science
```
{{< web >}}
-This example is from a side project
-that uses school data.
-I have trimmed some lines out
-of the request so it will fit better
-on the screen,
-and I did some slight reformatting
-to make the content a bit clearer.
+This example is from a side project that uses school data. I have trimmed some lines out of the request so it will fit better on the screen, and I did some slight reformatting to make the content a bit clearer.
{{< /web >}}
-When Django receives a request like this,
-it will parse the data
-and store it in an `HttpRequest` instance.
-The request provides convenient access
-to all parts
-of the raw data
-with helpful attributes
-for the most commonly used parameters.
-When considering the example,
-the request would have:
-
-* `method` - This matches the HTTP method of `POST`
- and can be used to act
- on the *kind* of request the user sent.
-* `content_type` - This attribute instructs Django
- on how to handle the data
- in the request.
- The example value would be `application/x-www-form-urlencoded `
- to indicate that this is user-submitted form data.
-* `POST` - For POST requests,
- Django processes the form data
- and stores the data
- into a dictionary-like structure.
- `request.POST['name']` would be `Science`
- in our example.
-* `GET` - Anything added to the query string
- (i.e., the content after a `?` character
- such as `student=Matt`
- in `/courses/?student=Matt`) is stored
- in a dictionary-like attribute as well.
-* `headers` - This is where all the HTTP headers
- like `Host`, `Accept-Language`,
- and the others are stored.
- `headers` is also dictionary-like
- and can be accessed like `request.headers['Host']`.
-
-Other attributes are available
-to `HttpRequest`,
-but that list will get you far enough to get started.
-Check out
-{{< extlink "https://docs.djangoproject.com/en/4.1/ref/request-response/" "Request and response objects" >}}
-for the other attributes.
-
-I should also note
-that `HttpRequest` instances are a common place
-to attach extra data.
-Django requests pass through many pieces
-in the framework.
-This makes the objects great candidates
-for extra features that you may require.
-For instance,
-if you need user management
+When Django receives a request like this, it will parse the data and store it in an `HttpRequest` instance. The request provides convenient access to all parts of the raw data with helpful attributes for the most commonly used parameters. When considering the example, the request would have:
+
+* `method` - This matches the HTTP method of `POST` and can be used to act on the *kind* of request the user sent.
+* `content_type` - This attribute instructs Django on how to handle the data in the request. The example value would be `application/x-www-form-urlencoded ` to indicate that this is user-submitted form data.
+* `POST` - For POST requests, Django processes the form data and stores the data into a dictionary-like structure. `request.POST['name']` would be `Science` in our example.
+* `GET` - Anything added to the query string (i.e., the content after a `?` character such as `student=Matt` in `/courses/?student=Matt`) is stored in a dictionary-like attribute as well.
+* `headers` - This is where all the HTTP headers like `Host`, `Accept-Language`, and the others are stored. `headers` is also dictionary-like and can be accessed like `request.headers['Host']`.
+
+Other attributes are available to `HttpRequest`, but that list will get you far enough to get started. Check out {{< extlink "https://docs.djangoproject.com/en/4.1/ref/request-response/" "Request and response objects" >}} for the other attributes.
+
+I should also note that `HttpRequest` instances are a common place to attach extra data. Django requests pass through many pieces in the framework. This makes the objects great candidates for extra features that you may require. For instance, if you need user management
{{< web >}}
(which we will explore in a future article),
{{< /web >}}
{{< book >}}
(which we will explore in a future chapter),
{{< /book >}}
-there is code
-that can attach a `request.user` attribute
-to represent a user
-in your system.
-It's *very* handy.
+there is code that can attach a `request.user` attribute to represent a user in your system. It's *very* handy.
-You can think of `HttpRequest` objects
-as the common interface
-for most of the inputs
-that my code uses.
+You can think of `HttpRequest` objects as the common interface for most of the inputs that my code uses.
## HttpResponse
-The other major interface
-that your views will use
-either directly or indirectly
-is the `HttpResponse` interface.
+The other major interface that your views will use either directly or indirectly is the `HttpResponse` interface.
-Your job as a Django user
-is to make your views return
-some kind of `HttpResponse`.
-A response instance will include all the necessary information
-to create a valid HTTP response
-for a user's browser.
+Your job as a Django user is to make your views return some kind of `HttpResponse`. A response instance will include all the necessary information to create a valid HTTP response for a user's browser.
Some of the common `HttpResponse` attributes include:
-* `status_code` - This is the HTTP status code.
- Status codes are a set of numbers
- that HTTP defines
- to tell a client (e.g., a browser)
- about the success or failure
- of a request.
- `200` is the usual success code.
- Any number from `400` and up will indicate some error,
- like `404` when a requested resource is not found.
-* `content` - This is the content
- that you provide to the user.
- The response stores this data as bytes.
- If you supply Python string data,
- Django will encode it to bytes for you.
+* `status_code` - This is the HTTP status code. Status codes are a set of numbers that HTTP defines to tell a client (e.g., a browser) about the success or failure of a request. `200` is the usual success code. Any number from `400` and up will indicate some error, like `404` when a requested resource is not found.
+* `content` - This is the content that you provide to the user. The response stores this data as bytes. If you supply Python string data, Django will encode it to bytes for you.
```python
>>> from django.http import HttpResponse
@@ -298,41 +133,15 @@ Some of the common `HttpResponse` attributes include:
b'Hello World'
```
-When working with Django views,
-you won't always use `HttpResponse` directly.
-`HttpResponse` has a variety
-of subclasses
-for common uses.
-Let's look at some:
-
-* `HttpResponseRedirect` - You may want to send a user
- to a different page.
- Perhaps the user bought something
- on your site,
- and you would like them to see a receipt page
- of their order.
- This subclass is perfect
- for that scenario.
-* `HttpResponseNotFound` - This is the subclass used
- to create a `404 Not Found` response.
- Django provides some helper functions to return this
- so you might not use this subclass directly,
- but it's good to know it's available.
-* `HttpResponseForbidden` - This type of response can be used
- when you don't want a user
- to access a part
- of your website
- (i.e., HTTP status `403 Forbidden`).
-
-Aside from the subclasses,
-Django has other techniques
-to return `HttpResponse` instances
-without creating one yourself.
-The most common function is `render`.
-
-`render` is a tool
-for working with templates.
-Templates are the topic
+When working with Django views, you won't always use `HttpResponse` directly. `HttpResponse` has a variety of subclasses for common uses. Let's look at some:
+
+* `HttpResponseRedirect` - You may want to send a user to a different page. Perhaps the user bought something on your site, and you would like them to see a receipt page of their order. This subclass is perfect for that scenario.
+* `HttpResponseNotFound` - This is the subclass used to create a `404 Not Found` response. Django provides some helper functions to return this so you might not use this subclass directly, but it's good to know it's available.
+* `HttpResponseForbidden` - This type of response can be used when you don't want a user to access a part of your website (i.e., HTTP status `403 Forbidden`).
+
+Aside from the subclasses, Django has other techniques to return `HttpResponse` instances without creating one yourself. The most common function is `render`.
+
+`render` is a tool for working with templates. Templates are the topic
{{< web >}}
of the next article,
{{< /web >}}
@@ -341,15 +150,7 @@ of the next chapter,
{{< /book >}}
but here is a sneak peek.
-You could write a view
-for a webpage
-and include a lot of HTML
-in your Python.
-HTML is the markup language
-of internet pages
-that we use
-to describe the format
-of a page.
+You could write a view for a webpage and include a lot of HTML in your Python. HTML is the markup language of internet pages that we use to describe the format of a page.
This view might look like:
@@ -368,26 +169,13 @@ def my_html_view(request):
return HttpResponse(response_content)
```
-While this works,
-it has many shortcomings.
-
-1. The HTML chunk isn't reusable by other views.
- That doesn't matter much for this small example,
- but it would be a huge problem
- when you try to make many views
- that use a lot of markup
- and need to share a common look.
-2. The mixing of Python and HTML is going to get messy.
- Need proof?
- Go look at computing history
- and learn about {{< extlink "https://en.wikipedia.org/wiki/Common_Gateway_Interface" "CGI" >}}.
- It wasn't pretty.
-3. How can you join pieces of HTML together?
- Not easily.
-
-With templates,
-we can separate the layout
-from the logic.
+While this works, it has many shortcomings:
+
+1. The HTML chunk isn't reusable by other views. That doesn't matter much for this small example, but it would be a huge problem when you try to make many views that use a lot of markup and need to share a common look.
+2. The mixing of Python and HTML is going to get messy. Need proof? Go look at computing history and learn about {{< extlink "https://en.wikipedia.org/wiki/Common_Gateway_Interface" "CGI" >}}. It wasn't pretty.
+3. How can you join pieces of HTML together? Not easily.
+
+With templates, we can separate the layout from the logic:
```python
# application/views.py
@@ -418,16 +206,9 @@ The important part for this article is not about the templates themselves.
{{< book >}}
The important part for this chapter is not about the templates themselves.
{{< /book >}}
-What's worth noting is that `render`
-loads the content from `template.html`,
-gets the output,
-and adds that output to an `HttpResponse` instance.
+What's worth noting is that `render` loads the content from `template.html`, gets the output, and adds that output to an `HttpResponse` instance.
-That wraps up `HttpRequest` and `HttpResponse`.
-With those building blocks,
-we can now look at other ways
-that you can make Django views
-for your project.
+That wraps up `HttpRequest` and `HttpResponse`. With those building blocks, we can now look at other ways that you can make Django views for your project.
## View Classes
@@ -437,18 +218,9 @@ By now we've seen this relationship with views:
HttpRequest -> view -> HttpResponse
```
-Views do not need to be functions exclusively.
-Django also provides tools
-to make views out of classes.
-These types of views derive
-from Django's `View` class.
+Views do not need to be functions exclusively. Django also provides tools to make views out of classes. These types of views derive from Django's `View` class.
-When you write a class-based view
-(often abbreviated to CBVs),
-you add instance methods
-that match up
-with HTTP methods.
-Let's see an example:
+When you write a class-based view (often abbreviated to CBVs), you add instance methods that match up with HTTP methods. Let's see an example:
```python
# application/views.py
@@ -460,22 +232,7 @@ class SampleView(View):
return HttpResponse("Hello from a CBV!")
```
-The `get` method
-on the class
-corresponds to a `GET` HTTP request.
-`*args` and `**kwargs` are a common convention
-in Python
-to make a method or function
-that accepts any number
-of positional or keyword based arguments.
-We need these to match the expect method signature
-that Django requires for CBVs.
-Similarly,
-you would write a `post` method
-to respond to a `POST` HTTP request
-and so on.
-With that view defined,
-we can connect it to a URLconf:
+The `get` method on the class corresponds to a `GET` HTTP request. `*args` and `**kwargs` are a common convention in Python to make a method or function that accepts any number of positional or keyword based arguments. We need these to match the expect method signature that Django requires for CBVs. Similarly, you would write a `post` method to respond to a `POST` HTTP request and so on. With that view defined, we can connect it to a URLconf:
```python
# project/urls.py
@@ -488,75 +245,30 @@ urlpatterns = [
]
```
-Note that we don't pass `SampleView`
-to `path` as is.
-`path` expects a callable object,
-so we must call `as_view`,
-a class method
-that returns a function
-that will call the code
-in our class.
-
-At this point,
-I would be suitably unimpressed
-if I were in your shoes.
-Why would we add all
-this boilerplate code
-when you can make a function
-and be done?
-If this were the full story,
-I would absolutely agree with you.
-A class-based view doesn't add much
-beyond the function-based version.
-If anything,
-CBVs have more to remember,
-so they are probably more confusing.
-
-Where class-based views begin
-to shine
-is when using some other classes
-beyond the initial `View` class.
-
-Django includes a host
-of class-based views
-to use for a variety
-of purposes.
-We can explore a few
-of them
-with our limited exposure
-to the full framework so far.
+Note that we don't pass `SampleView` to `path` as is. `path` expects a callable object, so we must call `as_view`, a class method that returns a function that will call the code in our class.
+
+At this point, I would be suitably unimpressed if I were in your shoes. Why would we add all this boilerplate code when you can make a function and be done? If this were the full story, I would absolutely agree with you. A class-based view doesn't add much beyond the function-based version. If anything, CBVs have more to remember, so they are probably more confusing.
+
+Where class-based views begin to shine is when using some other classes beyond the initial `View` class.
+
+Django includes a host of class-based views to use for a variety of purposes. We can explore a few of them with our limited exposure to the full framework so far.
## Out Of The Box Views
-I won't exhaustively cover all the class-based views
-because there are many.
-Also,
+I won't exhaustively cover all the class-based views because there are many. Also,
{{< web >}}
-if you're joining this article series
-from the beginning
-and have never done Django before,
+if you're joining this article series from the beginning and have never done Django before,
{{< /web >}}
{{< book >}}
if you have never done Django before,
{{< /book >}}
-then there will still be holes
-in your knowledge
-(which we will plug together!),
-and some of the views will not make much sense.
+then there will still be holes in your knowledge (which we will plug together!), and some of the views will not make much sense.
### RedirectView
-Use `RedirectView` to send users
-of your site
-to a different place.
-You *could* make a view
-that returns an `HttpResponseRedirect` instance,
-but this class-based view can handle that for you.
+Use `RedirectView` to send users of your site to a different place. You *could* make a view that returns an `HttpResponseRedirect` instance, but this class-based view can handle that for you.
-In fact,
-you can use `RedirectView`
-without subclassing it.
-Check this out:
+In fact, you can use `RedirectView` without subclassing it. Check this out:
```python
# project/urls.py
@@ -573,17 +285,9 @@ urlpatterns = [
]
```
-`RedirectView` can use `url`
-for a full URL,
-or it can use `pattern_name`
-if you need to route
-to a view
-that moved somewhere else
-in your project.
+`RedirectView` can use `url` for a full URL, or it can use `pattern_name` if you need to route to a view that moved somewhere else in your project.
-`as_view` is what lets us avoid subclassing `RedirectView`.
-The arguments passed to `as_view` override any class attributes.
-The following two `RedirectView` uses are equivalent:
+`as_view` is what lets us avoid subclassing `RedirectView`. The arguments passed to `as_view` override any class attributes. The following two `RedirectView` uses are equivalent:
```python
# project/urls.py
@@ -611,15 +315,9 @@ Earlier in the article,
{{< book >}}
Earlier in the chapter,
{{< /book >}}
-we briefly saw how to separate web page layout
-from the logic needed
-to build a page
-with templates.
+we briefly saw how to separate web page layout from the logic needed to build a page with templates.
-Templates are so commonly used
-that Django provides a class
-that knows how to produce a response
-with nothing more than a template name.
+Templates are so commonly used that Django provides a class that knows how to produce a response with nothing more than a template name.
An example looks like:
@@ -643,76 +341,33 @@ when we dive into templates.
### Other View Classes
-Django's other class-based views serve a variety
-of purposes.
-Django has views that will:
-
-* Display and handle HTML forms
- so users can input data
- and send the data
- to the application.
-* Pull data from a database
- and show an individual record
- to the user
- (e.g., a webpage to see facts about a particular movie).
-* Pull data from a database
- and show information
- from a collection of records
- to the user
- (e.g., showing the cast of actors from a movie).
-* Show data from specific time ranges
- like days, weeks, and months.
-
-As we continue to explore Django,
-We will discuss these views
-when their related topic (like forms) is the primary subject
+Django's other class-based views serve a variety of purposes. Django has views that will:
+
+* Display and handle HTML forms so users can input data and send the data to the application.
+* Pull data from a database and show an individual record to the user (e.g., a webpage to see facts about a particular movie).
+* Pull data from a database and show information from a collection of records to the user (e.g., showing the cast of actors from a movie).
+* Show data from specific time ranges like days, weeks, and months.
+
+As we continue to explore Django, We will discuss these views when their related topic (like forms) is the primary subject
{{< web >}}
of an article.
{{< /web >}}
{{< book >}}
of a chapter.
{{< /book >}}
-For now,
-when you're developing your own views,
-try to remember that Django probably has a class-based view
-to aid your work.
+For now, when you're developing your own views, try to remember that Django probably has a class-based view to aid your work.
## Useful View Decorators And Mixins
-Before we finish the tour
-of views,
-let's discuss some useful decorators and mixin classes.
-
-Decorators are a feature
-of Python
-(and many other languages)
-that let you extend a function
-with additional capabilities.
-A decorator can wrap a view function
-to provide new behavior
-to a view.
-Decorators are helpful
-when you have common functionality
-that you want to add to many views
-without copying and pasting a lot of code.
-
-Mixin classes serve a very similar purpose
-as decorators,
-but use Python's multiple inheritance feature
-of classes
-to "mix in" the new behavior
-with an existing class-based view.
+Before we finish the tour of views, let's discuss some useful decorators and mixin classes.
+
+Decorators are a feature of Python (and many other languages) that let you extend a function with additional capabilities. A decorator can wrap a view function to provide new behavior to a view. Decorators are helpful when you have common functionality that you want to add to many views without copying and pasting a lot of code.
+
+Mixin classes serve a very similar purpose as decorators, but use Python's multiple inheritance feature of classes to "mix in" the new behavior with an existing class-based view.
### Decorators To Know
-When you work
-with function-based views,
-there is a challenge
-when handling different HTTP methods.
-By default,
-a function based view can receive requests
-from *any* HTTP method.
-Some views will handle multiple methods like:
+When you work with function-based views, there is a challenge when handling different HTTP methods. By default, a function based view can receive requests from *any* HTTP method. Some views will handle multiple methods like:
```python
# application/views.py
@@ -729,12 +384,7 @@ def multi_method_view(request):
return HttpResponseNotAllowed()
```
-This view uses the `request` instance `method` attribute
-to check the request's HTTP method.
-What if you only want your view
-to respond to one HTTP method?
-Let's say you only want to respond to a POST.
-We could write:
+This view uses the `request` instance `method` attribute to check the request's HTTP method. What if you only want your view to respond to one HTTP method? Let's say you only want to respond to a POST. We could write:
```python
# application/views.py
@@ -758,12 +408,7 @@ def if_clause_view(request):
return HttpResponseNotAllowed()
```
-Both techniques work,
-but the code is a little messier
-because of the extra indentation.
-Instead, we can use the `require_POST` decorator
-and let Django check the method
-for us.
+Both techniques work, but the code is a little messier because of the extra indentation. Instead, we can use the `require_POST` decorator and let Django check the method for us:
```python
# application/views.py
@@ -775,22 +420,9 @@ def the_view(request):
return HttpResponse('Method was a POST.')
```
-This version states the expectation up front
-with the decorator
-and declares the contract
-that the view will work with.
-If a user tries a different method
-(like a `GET`),
-then Django will respond
-with HTTP status code `405`,
-which is an error code for "method not allowed."
-
-Another common decorator you may encounter is the `login_required` decorator.
-When we get to the subject
-of user management,
-you'll see that we can make a protected view
-for an app
-by including this decorator.
+This version states the expectation up front with the decorator and declares the contract that the view will work with. If a user tries a different method (like a `GET`), then Django will respond with HTTP status code `405`, which is an error code for "method not allowed."
+
+Another common decorator you may encounter is the `login_required` decorator. When we get to the subject of user management, you'll see that we can make a protected view for an app by including this decorator:
```python
# application/views.py
@@ -802,19 +434,9 @@ def the_view(request):
return HttpResponse('This view is only viewable to authenticated users.')
```
-Any unauthenticated user will be redirected automatically
-to the login page
-for your web app.
+Any unauthenticated user will be redirected automatically to the login page for your web app.
-A final example
-of a useful built-in decorator
-is `user_passes_test`.
-This is another decorator used
-with the user management system
-that lets us control *which* users should be allowed
-to access a view.
-For instance,
-we could make a view that only staff-level users could access.
+A final example of a useful built-in decorator is `user_passes_test`. This is another decorator used with the user management system that lets us control *which* users should be allowed to access a view. For instance, we could make a view that only staff-level users could access:
```python
# application/views.py
@@ -826,18 +448,9 @@ def the_view(request):
return HttpResponse('Only visible to staff users.')
```
-The decorator takes a callable
-that will accept a single argument
-of a user object.
-The view will only be accessible
-if the return value of the test callable evaluates to `True`.
+The decorator takes a callable that will accept a single argument of a user object. The view will only be accessible if the return value of the test callable evaluates to `True`.
-What I'm trying to show
-with these examples
-is how single decorators can quickly augment your views
-with new features.
-And, because of how decorators work to wrap functions,
-you can "stack" these together.
+What I'm trying to show with these examples is how single decorators can quickly augment your views with new features. And, because of how decorators work to wrap functions, you can "stack" these together:
```python
# application/views.py
@@ -853,23 +466,9 @@ def the_view(request):
### Mixins To Know
-Mixin classes are to class-based views
-as decorators are to function-based views.
-This isn't *completely* true
-since class-based views can also use decorators,
-but it should give you an idea
-of where mixins fit.
-
-Like the `login_required`
-and `user_passes_test` decorators,
-we have mixin equivalents
-of `LoginRequiredMixin`
-and `UserPassesTestMixin`.
-Maybe you have some template views
-that should only be accessible
-to authenticated users
-or staff-level users.
-Those views could look like:
+Mixin classes are to class-based views as decorators are to function-based views. This isn't *completely* true since class-based views can also use decorators, but it should give you an idea of where mixins fit.
+
+Like the `login_required` and `user_passes_test` decorators, we have mixin equivalents of `LoginRequiredMixin` and `UserPassesTestMixin`. Maybe you have some template views that should only be accessible to authenticated users or staff-level users. Those views could look like:
```python
# application/views.py
@@ -886,41 +485,15 @@ class StaffProtectedView(UserPassesTestMixin, TemplateView):
return self.request.user.is_staff
```
-You can see that these views are similar
-to their decorator counterparts
-with a slightly different usage pattern.
-
-One thing worth noting
-with mixins
-is their placement.
-Because of the way
-that Python handles multiple inheritance,
-you should be sure to include mixin classes
-to the left
-in the list of inherited base classes.
-This will ensure that Python will behave appropriately
-with these classes.
-The exact reason for this placement is
-because of Python's method resolution order (MRO) rules
-when using multiple inheritance.
-MRO is outside of our scope,
-but that's what you can search for
-if you want to learn more.
-
-There are plenty
-of other mixin classes.
-Most of Django's built-in class-based views are constructed
-by composing various mixin classes together.
-If you'd like to see how they are constructed,
-check out {{< extlink "https://ccbv.co.uk/" "Classy Class-Based Views" >}},
-a site showing the built-in CBVs
-and the mixins and attributes available
-to those classes.
+You can see that these views are similar to their decorator counterparts with a slightly different usage pattern.
+
+One thing worth noting with mixins is their placement. Because of the way that Python handles multiple inheritance, you should be sure to include mixin classes to the left in the list of inherited base classes. This will ensure that Python will behave appropriately with these classes. The exact reason for this placement is because of Python's method resolution order (MRO) rules when using multiple inheritance. MRO is outside of our scope, but that's what you can search for if you want to learn more.
+
+There are plenty of other mixin classes. Most of Django's built-in class-based views are constructed by composing various mixin classes together. If you'd like to see how they are constructed, check out {{< extlink "https://ccbv.co.uk/" "Classy Class-Based Views" >}}, a site showing the built-in CBVs and the mixins and attributes available to those classes.
## Summary
-That's a wrap on view fundamentals.
-We've looked at:
+That's a wrap on view fundamentals. We've looked at:
* View functions
* `HttpRequest` and `HttpResponse`
@@ -934,12 +507,7 @@ In the next article,
{{< book >}}
In the next chapter,
{{< /book >}}
-we'll see how views can mix static layout
-with the dynamic data we provide
-by using templates.
-Templates are the workhorse
-for your Django-based user interfaces.
-We're going to see:
+we'll see how views can mix static layout with the dynamic data we provide by using templates. Templates are the workhorse for your Django-based user interfaces. We're going to see:
* How to set up templates for your site
* Ways to call templates from views
@@ -949,15 +517,6 @@ We're going to see:
* Customizing templates with your own code extensions
{{< web >}}
-If you'd like to follow along
-with the series,
-please feel free to sign up
-for my newsletter
-where I announce all of my new content.
-If you have other questions,
-you can reach me online
-on Twitter
-where I am
-{{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
+If you'd like to follow along with the series, please feel free to sign up for my newsletter where I announce all of my new content. If you have other questions, you can reach me online on Twitter where I am {{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
{{< /web >}}
From 1dc299a403506b0ba088d98b3ab254e7283fe27d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nazar=C3=A9=20da=20Piedade?=
Date: Fri, 5 May 2023 11:09:42 +0100
Subject: [PATCH 05/30] chore: format
`understand-django/2020-04-02-templates-user-interfaces.pt.md`
---
...2020-04-02-templates-user-interfaces.pt.md | 773 +++---------------
1 file changed, 124 insertions(+), 649 deletions(-)
diff --git a/content/understand-django/2020-04-02-templates-user-interfaces.pt.md b/content/understand-django/2020-04-02-templates-user-interfaces.pt.md
index 144e4431..40287b34 100644
--- a/content/understand-django/2020-04-02-templates-user-interfaces.pt.md
+++ b/content/understand-django/2020-04-02-templates-user-interfaces.pt.md
@@ -1,14 +1,7 @@
---
title: "Templates For User Interfaces"
description: >-
- When your Django application
- sends back a response
- with your user interface,
- templates are the tool you'll use
- to produce that user interface.
- This article looks
- at what templates are
- and how to use them.
+ When your Django application sends back a response with your user interface, templates are the tool you'll use to produce that user interface. This article looks at what templates are and how to use them.
image: img/django.png
type: post
categories:
@@ -22,44 +15,17 @@ tags:
---
{{< web >}}
-In the previous
-[Understand Django]({{< ref "/understand-django/_index.md" >}})
-article,
-we looked at the fundamentals
-of using views in Django.
-This article will focus
-on templates.
+In the previous [Understand Django]({{< ref "/understand-django/_index.md" >}}) article, we looked at the fundamentals of using views in Django. This article will focus on templates.
{{< /web >}}
-Templates are your primary tool
-in a Django project
-for generating a user interface.
-With templates,
-you'll be able to build the pages
-that users will see when they visit your web app.
-Let's see how templates hook into views
-and what features Django provides
-with its template system.
+Templates are your primary tool in a Django project for generating a user interface. With templates, you'll be able to build the pages that users will see when they visit your web app. Let's see how templates hook into views and what features Django provides with its template system.
{{< understand-django-series "templates" >}}
## Set Up Templates
-We need a place for templates to live.
-Templates are static files
-that Django will fill in
-with data.
-In order to use those files,
-we must instruct Django
-on where to find them.
-
-Like most parts of Django,
-this configuration is
-in your project's settings file.
-After you use `startproject`,
-you can find a section
-in your settings file
-that will be called `TEMPLATES`.
-The section should look something like:
+We need a place for templates to live. Templates are static files that Django will fill in with data. In order to use those files, we must instruct Django on where to find them.
+
+Like most parts of Django, this configuration is in your project's settings file. After you use `startproject`, you can find a section in your settings file that will be called `TEMPLATES`. The section should look something like:
```python
# project/settings.py
@@ -79,54 +45,13 @@ TEMPLATES = [{
}]
```
-Django's template system can use multiple template backends.
-The backends dictate how your templates will work.
-I would recommend sticking
-with the default Django template language.
-This language has the tightest integration
-with the framework
-and the strongest support.
-
-The next thing to notice is `APP_DIRS`
-with its value of `True`.
-For the Django template language,
-setting this value to `True` will cause Django
-to look for template files
-within a `templates` directory
-in each Django application
-in your project.
-Note that this also includes any third party applications
-so you should probably leave this set to `True`.
-
-So,
-where should *your* templates go?
-There are different schools
-of thought
-in the Django community.
-Some developers believe in having all templates
-within applications.
-Others ascribe to having all your project's templates
-in a single directory.
-I'm in this second category of developers.
-I find it valuable
-to keep all
-of the templates for my entire project
-within a single directory.
-
-From my perspective,
-keeping templates in a single directory
-makes it very clear
-where all the layout and UI
-in your system will live.
-To use that pattern,
-we must set the `DIRS` variable
-with the directory
-that we want Django to include.
-I recommend keeping a `templates` directory
-at the root of your project.
-If you do that,
-your `DIRS` value will change
-to something like:
+Django's template system can use multiple template backends. The backends dictate how your templates will work. I would recommend sticking with the default Django template language. This language has the tightest integration with the framework and the strongest support.
+
+The next thing to notice is `APP_DIRS` with its value of `True`. For the Django template language, setting this value to `True` will cause Django to look for template files within a `templates` directory in each Django application in your project. Note that this also includes any third party applications so you should probably leave this set to `True`.
+
+So, where should *your* templates go? There are different schools of thought in the Django community. Some developers believe in having all templates within applications. Others ascribe to having all your project's templates in a single directory. I'm in this second category of developers. I find it valuable to keep all of the templates for my entire project within a single directory.
+
+From my perspective, keeping templates in a single directory makes it very clear where all the layout and UI in your system will live. To use that pattern, we must set the `DIRS` variable with the directory that we want Django to include. I recommend keeping a `templates` directory at the root of your project. If you do that, your `DIRS` value will change to something like:
```python
# project/settings.py
@@ -138,12 +63,7 @@ TEMPLATES = [
]
```
-Finally,
-there is `OPTIONS`.
-Each backend can accept a variety
-of options.
-`startproject` sets a number of context processors.
-We'll come back to context processors later
+Finally, there is `OPTIONS`. Each backend can accept a variety of options. `startproject` sets a number of context processors. We'll come back to context processors later
{{< web >}}
in this article.
{{< /web >}}
@@ -151,23 +71,13 @@ in this article.
in this chapter.
{{< /book >}}
-With your templates set up,
-you're ready to go!
+With your templates set up, you're ready to go!
## Using Templates With Render
-Django builds your user interface
-by *rendering* a template.
-The idea behind rendering is
-that dynamic data is combined
-with a static template file
-to produce a final output.
+Django builds your user interface by *rendering* a template. The idea behind rendering is that dynamic data is combined with a static template file to produce a final output.
-To produce an `HttpResponse`
-that contains rendered output,
-we use the `render` function.
-Let's see an example
-in the form of a function-based view (FBV).
+To produce an `HttpResponse` that contains rendered output, we use the `render` function. Let's see an example in the form of a function-based view (FBV):
```python
# application/views.py
@@ -183,62 +93,27 @@ def hello_view(request):
)
```
-In this example,
-the view would use a template located
-in `templates/hello.txt`
-which could contain:
+In this example, the view would use a template located in `templates/hello.txt` which could contain:
```txt
Hello {{ name }}
```
-When this view responds
-to a request,
-a user would see "Hello Johnny"
-in their browser.
-There are some interesting things to note
-about this example.
-
-1. The template can be any plain text file type.
- Most often we will use HTML to make a user interface
- so you will often see `some_template.html`,
- but the Django template system can render on any type.
-2. In the process of rendering,
- Django took the context data dictionary
- and used its keys as variable names
- in the template.
- Because of special double curly brace syntax,
- the template backend swapped out `{{ name }}`
- for the literal value of "Johnny"
- that was in the context.
-
-This idea of mixing context and static layout is the core concept
-of working with templates.
+When this view responds to a request, a user would see "Hello Johnny" in their browser. There are some interesting things to note about this example.
+
+1. The template can be any plain text file type. Most often we will use HTML to make a user interface so you will often see `some_template.html`, but the Django template system can render on any type.
+2. In the process of rendering, Django took the context data dictionary and used its keys as variable names in the template. Because of special double curly brace syntax, the template backend swapped out `{{ name }}` for the literal value of "Johnny" that was in the context.
+
+This idea of mixing context and static layout is the core concept of working with templates.
{{< web >}}
The rest of this article builds
{{< /web >}}
{{< book >}}
The rest of this chapter builds
{{< /book >}}
-on this root concept
-and shows what else is possible
-in the Django template language.
-
-As an aside,
-HTML is a topic that we are not going to explore directly.
-HTML, the Hypertext Markup Language, is the language used
-on the web
-to describe the structure of a page.
-HTML is composed of tags
-and many of these tags work in pairs.
-For example,
-to make a *paragraph*,
-you can use a `p` tag,
-which is represented
-by wrapping `p` with greater than and less than symbols
-to form the "opening" tag.
-The "closing" tag is similar,
-but it includes a forward slash.
+on this root concept and shows what else is possible in the Django template language.
+
+As an aside, HTML is a topic that we are not going to explore directly. HTML, the Hypertext Markup Language, is the language used on the web to describe the structure of a page. HTML is composed of tags and many of these tags work in pairs. For example, to make a *paragraph*, you can use a `p` tag, which is represented by wrapping `p` with greater than and less than symbols to form the "opening" tag. The "closing" tag is similar, but it includes a forward slash.
```html
This is a paragraph example.
@@ -250,19 +125,7 @@ From the last article,
{{< book >}}
From the last chapter,
{{< /book >}}
-you may recall seeing the `TemplateView`.
-In those examples,
-we provided a template name,
-and I declared that Django would take care
-of the rest.
-Now you can start to understand
-that Django takes the template name
-and calls code similar to `render`
-to provide an `HttpResponse`.
-Those examples were missing context data
-to combine with the template.
-A fuller example replicating the `hello_view` function-based view
-as a class-based-view would look like:
+you may recall seeing the `TemplateView`. In those examples, we provided a template name, and I declared that Django would take care of the rest. Now you can start to understand that Django takes the template name and calls code similar to `render` to provide an `HttpResponse`. Those examples were missing context data to combine with the template. A fuller example replicating the `hello_view` function-based view as a class-based-view would look like:
```python
# application/views.py
@@ -283,43 +146,19 @@ class HelloView(TemplateView):
return context
```
-This example uses `get_context_data`
-so that we can insert our "dynamic" data
-into the rendering system
-to give us the response we want.
+This example uses `get_context_data` so that we can insert our "dynamic" data into the rendering system to give us the response we want.
-In a real application,
-a lot of the code that we need to write focuses
-on building up a truly dynamic context.
-I'm using static data in these examples
-to keep the mechanics of the template system clear.
-When you see me use `context`,
-try to imagine more complex data building
-to create a user interface.
+In a real application, a lot of the code that we need to write focuses on building up a truly dynamic context. I'm using static data in these examples to keep the mechanics of the template system clear. When you see me use `context`, try to imagine more complex data building to create a user interface.
-Those are the fundamentals of rendering.
-We'll now turn our attention
-to what the Django template language is capable of.
+Those are the fundamentals of rendering. We'll now turn our attention to what the Django template language is capable of.
## Templates In Action
-When using templates,
-we take context data
-and insert it
-into the placeholders
-within the template.
+When using templates, we take context data and insert it into the placeholders within the template.
-Template variables are the most basic form
-of filling placeholders with context.
-The previous section showed an example
-by using the `name` variable.
-The context dictionary contains a `name` key,
-whose value appears anywhere in the template
-where that key is surrounded by double curly braces.
+Template variables are the most basic form of filling placeholders with context. The previous section showed an example by using the `name` variable. The context dictionary contains a `name` key, whose value appears anywhere in the template where that key is surrounded by double curly braces.
-We can also use a dot access
-when the context data is more complex.
-Let's say your template gets context like:
+We can also use a dot access when the context data is more complex. Let's say your template gets context like:
```python
context = {
@@ -332,14 +171,7 @@ context = {
}
```
-Your Django template *won't* work
-if you try to access this context data
-like a regular dictionary
-(e.g., `{{ address['street'] }}`).
-Instead,
-you would use dot notation
-to get to the data
-in the dictionary.
+Your Django template *won't* work if you try to access this context data like a regular dictionary (e.g., `{{ address['street'] }}`). Instead, you would use dot notation to get to the data in the dictionary:
```txt
The address is:
@@ -355,29 +187,11 @@ The address is:
Beverly Hills, CA 90210
```
-Django templates also try
-to be flexible
-with the types of context data.
-You could also pass in a Python class instance
-like an `Address` class
-with attributes
-that are the same as the keys
-in our previous dictionary.
-The template would work the same.
-
-The core template language also includes some standard programming logic keywords
-by using *tags*.
-Template tags look like `{% some_tag %}`
-whereas template variables look like `{{ some_variable }}`.
-Variables are meant to be placeholders
-to fill in,
-but tags offer more power.
-
-We can start
-with two core tags, `if` and `for`.
-
-The `if` tag is for handling conditional logic
-that your template might need.
+Django templates also try to be flexible with the types of context data. You could also pass in a Python class instance like an `Address` class with attributes that are the same as the keys in our previous dictionary. The template would work the same.
+
+The core template language also includes some standard programming logic keywords by using *tags*. Template tags look like `{% some_tag %}` whereas template variables look like `{{ some_variable }}`. Variables are meant to be placeholders to fill in, but tags offer more power.
+
+We can start with two core tags, `if` and `for`. The `if` tag is for handling conditional logic that your template might need:
{{< web >}}
```django
@@ -394,23 +208,7 @@ that your template might need.
```
{{< /book >}}
-This example will only include this welcome message HTML header tag
-when the user is logged in
-to the application.
-We started the example
-with an `if` tag.
-Observe that the `if` tag requires a closing `endif` tag.
-Templates must respect whitespace
-since your layout might depend
-on that whitespace.
-The template language can't use whitespace
-to indicate scope
-like it can with Python
-so it uses closing tags instead.
-As you might guess,
-there are also `else` and `elif` tags
-that are accepted inside
-of an `if`/`endif` pair.
+This example will only include this welcome message HTML header tag when the user is logged in to the application. We started the example with an `if` tag. Observe that the `if` tag requires a closing `endif` tag. Templates must respect whitespace since your layout might depend on that whitespace. The template language can't use whitespace to indicate scope like it can with Python so it uses closing tags instead. As you might guess, there are also `else` and `elif` tags that are accepted inside of an `if`/`endif` pair:
{{< web >}}
```django
@@ -431,15 +229,9 @@ of an `if`/`endif` pair.
```
{{< /book >}}
-In this case, only one of the header tags will render
-depending on whether the user is authenticated or not.
+In this case, only one of the header tags will render depending on whether the user is authenticated or not.
-The other core tag
-to consider
-is the `for` loop tag.
-A `for` loop
-in Django templates
-behaves as you might expect.
+The other core tag to consider is the `for` loop tag. A `for` loop in Django templates behaves as you might expect:
{{< web >}}
```django
@@ -462,13 +254,7 @@ behaves as you might expect.
```
{{< /book >}}
-Django will loop over iterables
-like lists
-and let users output template responses
-for each entry in an iterable.
-If the example above had a list
-of `items`
-in the context like:
+Django will loop over iterables like lists and let users output template responses for each entry in an iterable. If the example above had a list of `items` in the context like:
```python
items = [
@@ -487,18 +273,7 @@ Then the output would look roughly like:
```
-Occasionally,
-you may want to take some specific action
-on a particular element
-in the `for` loop.
-Python's built in `enumerate` function isn't available directly
-in templates,
-but a special variable called `forloop` is available
-inside of a `for` tag.
-This `forloop` variable has some attributes
-like `first` and `last`
-that you can use to make templates behave differently
-on certain loop iterations.
+Occasionally, you may want to take some specific action on a particular element in the `for` loop. Python's built in `enumerate` function isn't available directly in templates, but a special variable called `forloop` is available inside of a `for` tag. This `forloop` variable has some attributes like `first` and `last` that you can use to make templates behave differently on certain loop iterations:
{{< web >}}
```django
@@ -526,26 +301,13 @@ Counting:
3 is last!
```
-Equipped with variables,
-`if` tags,
-and `for` tags,
-you should now have the ability to make some fairly powerful templates,
-but there's more!
+Equipped with variables, `if` tags, and `for` tags, you should now have the ability to make some fairly powerful templates, but there's more!
### More Context On Context
-In the setup
-of the templates settings,
-we glossed over context processors.
-Context processors are a valuable way
-to extend the context
-that is available
-to your templates
-when they are rendered.
+In the setup of the templates settings, we glossed over context processors. Context processors are a valuable way to extend the context that is available to your templates when they are rendered.
-Here's the set of context processors
-that Django's `startproject` command brings in
-by default.
+Here's the set of context processors that Django's `startproject` command brings in by default:
```python
'context_processors': [
@@ -556,19 +318,9 @@ by default.
],
```
-Context processors are functions
-(technically, callables, but let's focus on functions)
-that receive an `HttpRequest`
-and must return a dictionary.
-The returned dictionary merges
-with any other context
-that will be passed to your template.
+Context processors are functions (technically, callables, but let's focus on functions) that receive an `HttpRequest` and must return a dictionary. The returned dictionary merges with any other context that will be passed to your template.
-Conceptually,
-when preparing to render
-and given a `context` dictionary
-that was passed to `render`,
-the template system will do something like:
+Conceptually, when preparing to render and given a `context` dictionary that was passed to `render`, the template system will do something like:
```python
for processor in context_processors:
@@ -577,13 +329,9 @@ for processor in context_processors:
# Continue on to template rendering
```
-The actual code in the template system is more complex
-than this concept code sketch,
-but not by much!
+The actual code in the template system is more complex than this concept code sketch, but not by much!
-We can look
-at the actual definition of the `request` context processor included
-in that default list.
+We can look at the actual definition of the `request` context processor included in that default list:
```python
# django/template/context_processors.py
@@ -592,57 +340,27 @@ def request(request):
return {'request': request}
```
-That's it!
-Because of this context processor,
-the `request` object will be available
-as a variable
-to any template
-in your project.
-That's super powerful.
+That's it! Because of this context processor, the `request` object will be available as a variable to any template in your project. That's super powerful.
-The "dark side" of context processors is
-that they run for all requests.
-If you write a context processor
-that is slow and does a lot of computation,
-*every request* will suffer
-that performance impact.
-So use context processors carefully.
+The "dark side" of context processors is that they run for all requests. If you write a context processor that is slow and does a lot of computation, *every request* will suffer that performance impact. So use context processors carefully.
### Reusable Chunks Of Templates
-Now let's talk about one of the powerhouse features
-of the template system: reusable pieces.
+Now let's talk about one of the powerhouse features of the template system: reusable pieces.
-Think about a website.
-Most pages have a similar look and feel.
-They do this by repeating a lot of the same HTML,
-which is Hypertext Markup Language
-that defines the structure
-of a page.
-These pages also use the same CSS, Cascading Style Sheets,
-which define the styles that shape the look
-of the page elements.
+Think about a website. Most pages have a similar look and feel. They do this by repeating a lot of the same HTML, which is Hypertext Markup Language that defines the structure of a page. These pages also use the same CSS, Cascading Style Sheets, which define the styles that shape the look of the page elements.
-Imagine you're asked to manage a site
-and you need to create two separate pages.
-The homepage looks like:
+Imagine you're asked to manage a site and you need to create two separate pages. The homepage looks like:
```html
@@ -656,8 +374,7 @@ The homepage looks like:
```
-And here is a page to learn about the company
-behind the website.
+And here is a page to learn about the company behind the website:
```html
@@ -671,19 +388,9 @@ behind the website.
```
-These examples are tiny amounts of HTML,
-but what if you're asked to change the stylesheet
-from `styles.css`
-to a new stylesheet made
-by a designer called `better_styles.css`?
-You would have to update both places.
-Now think if there were 2,000 pages
-instead of 2 pages.
-Making big changes quickly across a site would be virtually impossible!
+These examples are tiny amounts of HTML, but what if you're asked to change the stylesheet from `styles.css` to a new stylesheet made by a designer called `better_styles.css`? You would have to update both places. Now think if there were 2,000 pages instead of 2 pages. Making big changes quickly across a site would be virtually impossible!
-Django helps you avoid this scenario entirely
-with a few tags.
-Let's make a new template called `base.html`.
+Django helps you avoid this scenario entirely with a few tags. Let's make a new template called `base.html`:
{{< web >}}
```django
@@ -714,9 +421,7 @@ Let's make a new template called `base.html`.
```
{{< /book >}}
-We've created a reusable template with the `block` tag!
-We can fix up our homepage
-to use this new template.
+We've created a reusable template with the `block` tag! We can fix up our homepage to use this new template:
{{< web >}}
```django
@@ -737,58 +442,18 @@ to use this new template.
```
{{< /book >}}
-This new version of the homepage *extends* the base template.
-All the template had to do was define its own version
-of the `main` block
-to fill in the content.
-We could do the exact same thing with the about page.
-
-If we revisit the task of replacing `styles.css`
-with `better_styles.css`,
-we can make the update in `base.html`
-and have that change apply
-to any templates
-that extend it.
-Even if there were 2,000 pages
-that all extended from `base.html`,
-changing the stylesheet would still be one line
-of code
-to change
-for an entire site.
-
-That's the power of Django's template extension system.
-Use `extend` when you need content
-that is mostly the same.
-Add a `block` section whenever you need to customize an extended page.
-You can extend a page by including multiple types of blocks.
-The example only shows a `main` block,
-but you might have pages that customize a `sidebar`, `header`, `footer`,
-or whatever might vary.
-
-Another powerful tool for reuse is the `include` tag.
-The `include` tag is useful
-when you want to extract some chunk
-of template
-that you want to use
-in multiple locations.
-You may want to use `include` to:
-
-1. Keep templates tidy.
- You can break a large template up into small pieces
- that are more manageable.
-2. Use a template fragment
- in different parts of your site.
- Maybe you have a piece of template
- that should only appear on a few pages.
-
-Coming back to our website example,
-imagine that `base.html` grew to be 20,000 lines long.
-Navigating to the right part
-of the template
-to make changes
-is now harder.
-We can decompose the template
-into smaller pieces.
+This new version of the homepage *extends* the base template. All the template had to do was define its own version of the `main` block to fill in the content. We could do the exact same thing with the about page.
+
+If we revisit the task of replacing `styles.css` with `better_styles.css`, we can make the update in `base.html` and have that change apply to any templates that extend it. Even if there were 2,000 pages that all extended from `base.html`, changing the stylesheet would still be one line of code to change for an entire site.
+
+That's the power of Django's template extension system. Use `extend` when you need content that is mostly the same. Add a `block` section whenever you need to customize an extended page. You can extend a page by including multiple types of blocks. The example only shows a `main` block, but you might have pages that customize a `sidebar`, `header`, `footer`, or whatever might vary.
+
+Another powerful tool for reuse is the `include` tag. The `include` tag is useful when you want to extract some chunk of template that you want to use in multiple locations. You may want to use `include` to:
+
+1. Keep templates tidy. You can break a large template up into small pieces that are more manageable.
+2. Use a template fragment in different parts of your site. Maybe you have a piece of template that should only appear on a few pages.
+
+Coming back to our website example, imagine that `base.html` grew to be 20,000 lines long. Navigating to the right part of the template to make changes is now harder. We can decompose the template into smaller pieces:
{{< web >}}
```django
@@ -817,52 +482,24 @@ into smaller pieces.
```
{{< /book >}}
-The `include` tag can move those extra pieces around.
-By providing a good name for your templates,
-if you needed to change the structure of some section
-like navigation,
-you could go to the template
-with the appropriate name.
-That template file would focus
-on only the element that you need to change.
+The `include` tag can move those extra pieces around. By providing a good name for your templates, if you needed to change the structure of some section like navigation, you could go to the template with the appropriate name. That template file would focus on only the element that you need to change.
-`block`, `extends`, and `include` are core tags
-for keeping your user interface code
-from sprawling all over the place
-with lots of duplication.
+`block`, `extends`, and `include` are core tags for keeping your user interface code from sprawling all over the place with lots of duplication.
-Next, let's talk about more
-of Django's built-in template tags
-that can supercharge your UI.
+Next, let's talk about more of Django's built-in template tags that can supercharge your UI.
## The Templates Toolbox
-The Django documentation includes
-a {{< extlink "https://docs.djangoproject.com/en/4.1/ref/templates/builtins/" "large set of built-in tags" >}}
-that you can use
-in your projects.
-We aren't going to cover all of them,
-but I'll focus
-on a few tags
-to give you a flavor
-of what is available.
-
-One of the most used built-in tags
-aside from what we've already covered
-is the `url` tag.
+The Django documentation includes a {{< extlink "https://docs.djangoproject.com/en/4.1/ref/templates/builtins/" "large set of built-in tags" >}} that you can use in your projects. We aren't going to cover all of them, but I'll focus on a few tags to give you a flavor of what is available.
+
+One of the most used built-in tags aside from what we've already covered is the `url` tag.
{{< web >}}
Recall from the article
{{< /web >}}
{{< book >}}
Recall from the chapter
{{< /book >}}
-on URLs
-that you can get the URL
-to a named view
-by using the `reverse` function.
-What if you wanted to use the URL
-in your template?
-You could do this:
+on URLs that you can get the URL to a named view by using the `reverse` function. What if you wanted to use the URL in your template? You could do this:
```python
# application/views.py
@@ -881,12 +518,7 @@ def the_view(request):
)
```
-While this works,
-it's tedious to have to route all URLs
-through the context.
-Instead,
-our template can directly create the proper URL.
-Here's what `a_template.html` might look like instead:
+While this works, it's tedious to have to route all URLs through the context. Instead, our template can directly create the proper URL. Here's what `a_template.html` might look like instead:
{{< web >}}
```django
@@ -899,24 +531,9 @@ Here's what `a_template.html` might look like instead:
```
{{< /book >}}
-The `url` tag is the template equivalent
-of the `reverse` function.
-Like its `reverse` counterpart,
-`url` can accept args or kwargs
-for routes
-that expect other variables.
-`url` is an incredibly useful tool
-and one that you will probably reach for many times
-as you build your user interface.
-
-Another useful tag is the `now` tag.
-`now` is a convenient method
-to display information
-about the current time.
-Using what Django calls *format specifiers*,
-you can tell your template how to display the current time.
-Want to add a current copyright year to your website?
-No problem!
+The `url` tag is the template equivalent of the `reverse` function. Like its `reverse` counterpart, `url` can accept args or kwargs for routes that expect other variables. `url` is an incredibly useful tool and one that you will probably reach for many times as you build your user interface.
+
+Another useful tag is the `now` tag. `now` is a convenient method to display information about the current time. Using what Django calls *format specifiers*, you can tell your template how to display the current time. Want to add a current copyright year to your website? No problem!:
{{< web >}}
```django
@@ -929,14 +546,7 @@ No problem!
```
{{< /book >}}
-One final built-in tag to consider is the `spaceless` tag.
-HTML is *partially* sensitive to whitespace.
-There are some frustrating circumstances
-where this whitespace sensitivity can ruin your day
-when building a user interface.
-Can you make a pixel perfect navigation menu
-for your site with an unordered list?
-Maybe. Consider this:
+One final built-in tag to consider is the `spaceless` tag. HTML is *partially* sensitive to whitespace. There are some frustrating circumstances where this whitespace sensitivity can ruin your day when building a user interface. Can you make a pixel perfect navigation menu for your site with an unordered list? Maybe. Consider this:
```html
@@ -945,12 +555,7 @@ Maybe. Consider this:
```
-The indented whitespace on those list items
-(or the new line characters that follow them)
-might cause you trouble
-when working with CSS.
-Knowing that the whitespace can affect layout,
-we can use `spaceless` like so:
+The indented whitespace on those list items (or the new line characters that follow them) might cause you trouble when working with CSS. Knowing that the whitespace can affect layout, we can use `spaceless` like so:
{{< web >}}
```django
@@ -973,29 +578,18 @@ we can use `spaceless` like so:
```
{{< /book >}}
-This neat little template tag will remove all the spaces
-between HTML tags
-so your output looks like:
+This neat little template tag will remove all the spaces between HTML tags so your output looks like:
```html
```
-By removing the extra space,
-you may get a more consistent experience
-with your CSS styling
-and save yourself some frustration.
+By removing the extra space, you may get a more consistent experience with your CSS styling and save yourself some frustration.
{{< web >}}
(I had to trim the output to fit better on the screen.)
{{< /web >}}
-There is another kind of built-in
-that we have not looked at yet.
-These alternative built-in functions are called **filters**.
-Filters change the output of variables
-in your templates.
-The filter syntax is a bit interesting.
-It looks like:
+There is another kind of built-in that we have not looked at yet. These alternative built-in functions are called **filters**. Filters change the output of variables in your templates. The filter syntax is a bit interesting. It looks like:
{{< web >}}
```django
@@ -1009,25 +603,9 @@ Here's a filter example:
```
{{< /book >}}
-The important element is the pipe character directly
-after a variable.
-This character signals to the template system
-that we want to modify the variable
-with some kind of transformation.
-Also observe that filters are used
-between double curly braces
-instead of the `{%` syntax
-that we've seen with tags.
-
-A very common filter is the `date` filter.
-When you pass a Python `datetime` instance
-in the context,
-you can use the `date` filter
-to control the format
-of the datetime.
-The `date` {{< extlink "https://docs.djangoproject.com/en/4.1/ref/templates/builtins/#date" "documentation" >}} shows
-what options you can use
-to modify the format.
+The important element is the pipe character directly after a variable. This character signals to the template system that we want to modify the variable with some kind of transformation. Also observe that filters are used between double curly braces instead of the `{%` syntax that we've seen with tags.
+
+A very common filter is the `date` filter. When you pass a Python `datetime` instance in the context, you can use the `date` filter to control the format of the datetime. The `date` {{< extlink "https://docs.djangoproject.com/en/4.1/ref/templates/builtins/#date" "documentation" >}} shows what options you can use to modify the format:
{{< web >}}
```django
@@ -1040,19 +618,9 @@ to modify the format.
```
{{< /book >}}
-If `a_datetime` was an instance of April Fools' Day,
-then it could return a string like `2020-04-01`.
-The `date` filter has many specifiers
-that will enable you to produce most
-of the date formatting outputs
-you could think of.
+If `a_datetime` was an instance of April Fools' Day, then it could return a string like `2020-04-01`. The `date` filter has many specifiers that will enable you to produce most of the date formatting outputs you could think of.
-`default` is a useful filter
-for when your template value evaluates to `False`.
-This is perfect when you've got a variable
-with an empty string.
-The example below outputs "Nothing to see here"
-if the variable was Falsy.
+`default` is a useful filter for when your template value evaluates to `False`. This is perfect when you've got a variable with an empty string. The example below outputs "Nothing to see here" if the variable was Falsy:
{{< web >}}
```django
@@ -1065,43 +633,22 @@ if the variable was Falsy.
```
{{< /book >}}
-Falsy is a concept in Python
-that describes anything
-that Python will evaluate as false
-in a boolean expression.
-Empty strings, empty lists, empty dicts, empty sets, `False`, and `None`
-are all common Falsy values.
+Falsy is a concept in Python that describes anything that Python will evaluate as false in a boolean expression. Empty strings, empty lists, empty dicts, empty sets, `False`, and `None` are all common Falsy values.
-`length` is a simple filter
-for lists.
-`{{ a_list_variable|length }}` will produce a number.
-It is the Django template equivalent to the `len` function.
+`length` is a simple filter for lists. `{{ a_list_variable|length }}` will produce a number. It is the Django template equivalent to the `len` function.
-I like the `linebreaks` filter a lot.
-If you create a form
+I like the `linebreaks` filter a lot. If you create a form
{{< web >}}
(which we'll explore in the next article)
{{< /web >}}
{{< book >}}
(which we'll explore in the next chapter)
{{< /book >}}
-and accept a text area field where the user is allowed
-to provide newlines,
-then the `linebreaks` filter allows you
-to display those newlines later
-when rendering the user's data.
-By default,
-HTML will not show new line characters as intended.
-The `linebreaks` filter will convert `\n`
-to a ` ` HTML tag.
-Handy!
-
-Before moving on,
-let's consider two more.
-
-`pluralize` is a convenient filter
-for the times when your text considers counts
-of things. Consider a count of items.
+and accept a text area field where the user is allowed to provide newlines, then the `linebreaks` filter allows you to display those newlines later when rendering the user's data. By default, HTML will not show new line characters as intended. The `linebreaks` filter will convert `\n` to a ` ` HTML tag. Handy!
+
+Before moving on, let's consider two more.
+
+`pluralize` is a convenient filter for the times when your text considers counts of things. Consider a count of items.
{{< web >}}
```django
@@ -1114,9 +661,7 @@ of things. Consider a count of items.
```
{{< /book >}}
-The `pluralize` filter will do the right thing
-if there are zero, one, or more items
-in the list.
+The `pluralize` filter will do the right thing if there are zero, one, or more items in the list.
```txt
0 items
@@ -1126,17 +671,9 @@ in the list.
(and so on)
```
-Be aware that `pluralize` can't handle irregular plurals
-like "mice" for "mouse."
+Be aware that `pluralize` can't handle irregular plurals like "mice" for "mouse."
-The final filter in our tour is the `yesno` filter.
-`yesno` is good for converting `True|False|None`
-into a meaningful text message.
-Imagine we're making an application
-for tracking events
-and a person's attendance is one
-of those three values.
-Our template might look like:
+The final filter in our tour is the `yesno` filter. `yesno` is good for converting `True|False|None` into a meaningful text message. Imagine we're making an application for tracking events and a person's attendance is one of those three values. Our template might look like:
{{< web >}}
```django
@@ -1149,25 +686,15 @@ Our template might look like:
```
{{< /book >}}
-Depending on the value of `user_accepted`,
-the template will display something meaningful
-to a reader.
+Depending on the value of `user_accepted`, the template will display something meaningful to a reader.
-There are so many built-ins
-that it's really hard to narrow down my favorites.
-Check out the full list
-to see what might be useful for you.
+There are so many built-ins that it's really hard to narrow down my favorites. Check out the full list to see what might be useful for you.
-What if the built-ins don't cover what you need?
-Have no fear,
-Django lets you make custom tags and filters
-for your own purposes.
-We'll see how next.
+What if the built-ins don't cover what you need? Have no fear, Django lets you make custom tags and filters for your own purposes. We'll see how next.
## Build Your Own Lightsaber In Templates
-When you need to build your own template tags or filters,
-Django gives you the tools to make what you need.
+When you need to build your own template tags or filters, Django gives you the tools to make what you need.
There are three major elements to working with custom tags:
@@ -1175,16 +702,7 @@ There are three major elements to working with custom tags:
2. Registering your tags with the template engine.
3. Loading your tags in a template so they can be used.
-The first step is to put the tags
-in the correct location.
-To do that,
-we need a `templatetags` Python package
-inside of a Django application.
-We also need a module
-in that directory.
-Choose the module name carefully
-because it is what we will load
-in the template later on.
+The first step is to put the tags in the correct location. To do that, we need a `templatetags` Python package inside of a Django application. We also need a module in that directory. Choose the module name carefully because it is what we will load in the template later on:
```txt
application
@@ -1197,10 +715,7 @@ application
└── views.py
```
-Next,
-we need to make our tag or filter
-and register it.
-Let's start with a filter example.
+Next, we need to make our tag or filter and register it. Let's start with a filter example:
```python
# application/templatetags/custom_tags.py
@@ -1220,13 +735,7 @@ def add_pizzazz(value):
return value + random.choice(pieces_of_flair)
```
-Now,
-if we have a `message` variable,
-we can give it some pizzazz.
-To use the custom filter,
-we must load our tags module
-into the template
-with the `load` tag.
+Now, if we have a `message` variable, we can give it some pizzazz. To use the custom filter, we must load our tags module into the template with the `load` tag:
{{< web >}}
```django
@@ -1243,14 +752,9 @@ with the `load` tag.
```
{{< /book >}}
-If our message was "You got a perfect score!",
-then our template should show the message
-and one of the three random choices
-like "You got a perfect score! Wowza!"
+If our message was "You got a perfect score!", then our template should show the message and one of the three random choices like "You got a perfect score! Wowza!"
-Writing basic custom tags is very similar
-to custom filters.
-Code will speak better than words here.
+Writing basic custom tags is very similar to custom filters. Code will speak better than words here:
```python
# application/templatetags/custom_tags.py
@@ -1273,7 +777,7 @@ def champion_welcome(name, level):
return welcome
```
-We can load the custom tags and use our tag like any other built-in tag.
+We can load the custom tags and use our tag like any other built-in tag:
{{< web >}}
```django
@@ -1290,30 +794,15 @@ We can load the custom tags and use our tag like any other built-in tag.
```
{{< /book >}}
-This silly welcome tag will respond
-to multiple input variables
-and vary depending on the provided level.
-The example usage should display "Hello great champion He-Man!"
-
-We're only looking at the most common kinds
-of custom tags
-in our examples.
-There are some more advanced custom tagging features
-which you can explore
-in the {{< extlink "https://docs.djangoproject.com/en/4.1/howto/custom-template-tags/" "Django custom template tags documentation" >}}.
-
-Django also uses `load`
-to provide template authors
-with some additional tools.
-For instance,
-we will see how to load some custom tags provided
-by the framework
-when we learn about working with images and JavaScript later on.
+This silly welcome tag will respond to multiple input variables and vary depending on the provided level. The example usage should display "Hello great champion He-Man!"
+
+We're only looking at the most common kinds of custom tags in our examples. There are some more advanced custom tagging features which you can explore in the {{< extlink "https://docs.djangoproject.com/en/4.1/howto/custom-template-tags/" "Django custom template tags documentation" >}}.
+
+Django also uses `load` to provide template authors with some additional tools. For instance, we will see how to load some custom tags provided by the framework when we learn about working with images and JavaScript later on.
## Summary
-Now we've seen templates in action!
-We've looked at:
+Now we've seen templates in action! We've looked at:
* How to set up templates for your site
* Ways to call templates from views
@@ -1328,12 +817,7 @@ In the next article,
{{< book >}}
In the next chapter,
{{< /book >}}
-we are going to examine
-how users can send data to a Django application
-with HTML forms.
-Django has tools
-to make form building quick and effective.
-We're going to see:
+we are going to examine how users can send data to a Django application with HTML forms. Django has tools to make form building quick and effective. We're going to see:
* The `Form` class that Django uses to handle form data in Python
* Controlling what fields are in forms
@@ -1341,15 +825,6 @@ We're going to see:
* How to do form validation
{{< web >}}
-If you'd like to follow along
-with the series,
-please feel free to sign up
-for my newsletter
-where I announce all of my new content.
-If you have other questions,
-you can reach me online
-on Twitter
-where I am
-{{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
+If you'd like to follow along with the series, please feel free to sign up for my newsletter where I announce all of my new content. If you have other questions, you can reach me online on Twitter where I am {{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
{{< /web >}}
From f87773a4fbfad6656a8df1d1eea57653b53f90bf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nazar=C3=A9=20da=20Piedade?=
Date: Fri, 5 May 2023 11:10:09 +0100
Subject: [PATCH 06/30] chore: format
`understand-django/2020-05-05-user-interaction-forms.pt.md`
---
.../2020-05-05-user-interaction-forms.pt.md | 658 +++---------------
1 file changed, 109 insertions(+), 549 deletions(-)
diff --git a/content/understand-django/2020-05-05-user-interaction-forms.pt.md b/content/understand-django/2020-05-05-user-interaction-forms.pt.md
index 137e6ca9..a8a21e24 100644
--- a/content/understand-django/2020-05-05-user-interaction-forms.pt.md
+++ b/content/understand-django/2020-05-05-user-interaction-forms.pt.md
@@ -1,16 +1,7 @@
---
title: "User Interaction With Forms"
description: >-
- How do users provide data
- to your website
- so you can interact
- with them?
- We can answer that question
- by exploring Django's form system,
- and the tools that Django provides
- to simplify your site
- as you engage
- with your users.
+ How do users provide data to your website so you can interact with them? We can answer that question by exploring Django's form system, and the tools that Django provides to simplify your site as you engage with your users.
image: img/django.png
type: post
categories:
@@ -24,72 +15,35 @@ tags:
---
{{< web >}}
-In the previous
-[Understand Django]({{< ref "/understand-django/_index.md" >}})
-article,
-we saw how Django templates work
-to produce a user interface.
-That's fine
-if you only need to display a user interface,
-but what do you do
+In the previous [Understand Django]({{< ref "/understand-django/_index.md" >}}) article, we saw how Django templates work to produce a user interface. That's fine if you only need to display a user interface, but what do you do
{{< /web >}}
{{< book >}}
What do you do
{{< /book >}}
-if you need your site
-to interact with users?
-You use Django's form system!
+if you need your site to interact with users? You use Django's form system!
{{< web >}}
In this article,
{{< /web >}}
{{< book >}}
In this chapter,
{{< /book >}}
-we'll focus
-on how to work with web forms
-using the Django form system.
+we'll focus on how to work with web forms using the Django form system.
-{{< understand-django-series "forms" >}}
+{{< understand-django-series-pt "forms" >}}
## Web Forms 101
-Before we can dive into how Django handles forms,
-we need to have an understanding of HTML forms
-in general.
-Django's form functionality builds upon web forms
-so this topic won't make sense
-without a baseline knowledge
-of the topic.
-
-HTML can describe the type
-of data
-that you may want your users
-to send to your site.
-Collecting this data is done
-with a handful of tags.
-The primary HTML tags to consider are `form`, `input`, and `select`.
-
-A `form` tag is the container
-for all the data
-that you want a user to send
-to your application.
-The tag has two critical attributes
-that tell the browser how to send data: `action` and `method`.
-
-`action` would be better named as "destination" or "url."
-Alas, we are stuck with `action`.
-This attribute of the `form` tag is where user data should be sent to.
-It's also useful to know that leaving out `action`
-or using `action=""` will send any form data
-as an HTTP request to the same URL
-that the user's browser is on.
-
-The `method` attribute dictates which HTTP method to use
-and can have a value of `GET` or `POST`.
-When paired with `action`,
-the browser knows how to send a properly formatted HTTP request.
-
-Let's say we have this example.
+Before we can dive into how Django handles forms, we need to have an understanding of HTML forms in general. Django's form functionality builds upon web forms so this topic won't make sense without a baseline knowledge of the topic.
+
+HTML can describe the type of data that you may want your users to send to your site. Collecting this data is done with a handful of tags. The primary HTML tags to consider are `form`, `input`, and `select`.
+
+A `form` tag is the container for all the data that you want a user to send to your application. The tag has two critical attributes that tell the browser how to send data: `action` and `method`.
+
+`action` would be better named as "destination" or "url." Alas, we are stuck with `action`. This attribute of the `form` tag is where user data should be sent to. It's also useful to know that leaving out `action` or using `action=""` will send any form data as an HTTP request to the same URL that the user's browser is on.
+
+The `method` attribute dictates which HTTP method to use and can have a value of `GET` or `POST`. When paired with `action`, the browser knows how to send a properly formatted HTTP request.
+
+Let's say we have this example:
```html
```
-When the form's method is `GET`,
-the form data will be sent as part
-of the URL
-in a querystring.
-The GET request sent to the server will look
-like `/some/form/?message=Hello`.
-This type of form submission is most useful
-when we don't need to save data
-and are trying to do some kind of query.
-For instance,
-you could give your application some search functionality
-with a URL like `/search/?q=thing+to+search`.
-These links could be bookmarked easily
-and are a natural fit
-for that kind of function.
-
-The `POST` method of sending form data is for data
-that we want to be secure or saved
-within an application.
-With a GET request,
-form data in the querystring is exposed
-in a number of places
-(see {{< extlink "https://owasp.org/www-community/vulnerabilities/Information_exposure_through_query_strings_in_url" "more information" >}}
-from the Open Web Application Security Project (OWASP)).
-On the other hand,
-POST sends the data in the body
-of the HTTP request.
-This means that if your site is secure
-(i.e., using HTTPS),
-then data is encrypted
-while traveling
-from a browser to a server.
-
-If you ever login to a website
-and submit a password
-in a form,
-you can be nearly certain
-that the form is sent
-with the POST method option
-(and if it's not, run away!).
-
-We've seen that `form` is the container
-that guides how to send form data.
-`input` and `select` are the tags
-that let us display a meaningful form
-to the user.
-
-The more prevalent tag is `input`.
-With the `input` tag,
-form authors will set `type` and `name` primarily.
-The `type` attribute tells the browser
-which kind of input to display.
+When the form's method is `GET`, the form data will be sent as part of the URL in a querystring. The GET request sent to the server will look like `/some/form/?message=Hello`. This type of form submission is most useful when we don't need to save data and are trying to do some kind of query. For instance, you could give your application some search functionality with a URL like `/search/?q=thing+to+search`. These links could be bookmarked easily and are a natural fit for that kind of function.
+
+The `POST` method of sending form data is for data that we want to be secure or saved within an application. With a GET request, form data in the querystring is exposed in a number of places (see {{< extlink "https://owasp.org/www-community/vulnerabilities/Information_exposure_through_query_strings_in_url" "more information" >}}
+from the Open Web Application Security Project (OWASP)). On the other hand, POST sends the data in the body of the HTTP request. This means that if your site is secure (i.e., using HTTPS), then data is encrypted while traveling from a browser to a server.
+
+If you ever login to a website and submit a password in a form, you can be nearly certain that the form is sent with the POST method option (and if it's not, run away!).
+
+We've seen that `form` is the container that guides how to send form data. `input` and `select` are the tags that let us display a meaningful form to the user.
+
+The more prevalent tag is `input`. With the `input` tag, form authors will set `type` and `name` primarily. The `type` attribute tells the browser which kind of input to display:
* Do we need a checkbox? `type="checkbox"`
* Do we need a password field that hides characters? `type="password"`
* How about a classic text box? `type="text"`
-You can see a full list of types
-on the {{< extlink "https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input" "MDN input documentation" >}} page.
-
-The other attribute, `name`, is the identifier
-that the form will pair with the user data.
-The server uses the identifier
-so it can distinguish
-between the pieces of data
-that a form submission may include.
-
-Another tag that your forms may use is the `select` tag.
-This kind of tag is less frequent
-than the `input` tag.
-The `select` tag lets users make a choice
-from a list of options.
-The default browser user interface
-for this tag is a dropdown menu.
-
-With these core elements
-of HTML forms,
-we are equipped
-to understand Django's form capabilities.
-Let's dive in!
+You can see a full list of types on the {{< extlink "https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input" "MDN input documentation" >}} page.
+
+The other attribute, `name`, is the identifier that the form will pair with the user data. The server uses the identifier so it can distinguish between the pieces of data that a form submission may include.
+
+Another tag that your forms may use is the `select` tag. This kind of tag is less frequent than the `input` tag. The `select` tag lets users make a choice from a list of options. The default browser user interface for this tag is a dropdown menu.
+
+With these core elements of HTML forms, we are equipped to understand Django's form capabilities. Let's dive in!
## Django Forms
-Django's form features act as a bridge
-between HTML forms
-and Python classes and data types.
-When presenting a form
-to a user via a view,
-the form system is able
-to display the proper HTML form tags
-and structure.
-When receiving this form data
-from a user's submission,
-the form system can translate the browser's raw form data
-into native Python data that we can use.
-
-We can begin
-with the `Form` class.
-A form class acts as the data declaration
-of what data we need
-from the user.
-Here's an example that we can examine.
+Django's form features act as a bridge between HTML forms and Python classes and data types. When presenting a form to a user via a view, the form system is able to display the proper HTML form tags and structure. When receiving this form data from a user's submission, the form system can translate the browser's raw form data into native Python data that we can use.
+
+We can begin with the `Form` class. A form class acts as the data declaration of what data we need from the user. Here's an example that we can examine:
```python
# application/forms.py
@@ -216,27 +96,10 @@ class ContactForm(forms.Form):
)
```
-* User-defined Django forms should subclass the `Form` class.
- This class adds a lot of powerful functionality
- that will aid us
- as we explore more.
-* The data that we want to collect is listed
- as class level attributes.
- Each field of the form is a certain field type
- that has its own characteristics
- to help translate raw form data
- into the data types
- that we want to work with in views.
-
-If we take this form
-and add it to a view's context as `form`,
-then we can render it in a template.
-The default rendering of the form uses an HTML table,
-but we can render the fields in a simpler format
-with the `as_p` method.
-This method will use paragraph tags instead
-for the form elements.
-If the template looks like:
+* User-defined Django forms should subclass the `Form` class. This class adds a lot of powerful functionality that will aid us as we explore more.
+* The data that we want to collect is listed as class level attributes. Each field of the form is a certain field type that has its own characteristics to help translate raw form data into the data types that we want to work with in views.
+
+If we take this form and add it to a view's context as `form`, then we can render it in a template. The default rendering of the form uses an HTML table, but we can render the fields in a simpler format with the `as_p` method. This method will use paragraph tags instead for the form elements. If the template looks like:
{{< web >}}
```django
@@ -261,33 +124,16 @@ Then Django will render:
```
-To make it possible to submit the form,
-we need to wrap this rendered output
-with a `form` tag
-and include a submit button
-and a CSRF token.
-
-Huh? *CSRF token?*
-Sadly,
-the world is full of nefarious people
-who would love to hack your application
-to steal data
-from others.
-A CSRF token is a security measure
-that Django includes to make it harder
-for malicious actors
-to tamper with your form's data.
-We'll talk more about security
+To make it possible to submit the form, we need to wrap this rendered output with a `form` tag and include a submit button and a CSRF token.
+
+Huh? *CSRF token?* Sadly, the world is full of nefarious people who would love to hack your application to steal data from others. A CSRF token is a security measure that Django includes to make it harder for malicious actors to tamper with your form's data. We'll talk more about security
{{< web >}}
in a future article.
{{< /web >}}
{{< book >}}
in a future chapter.
{{< /book >}}
-For now,
-sprinkle the token into your forms
-with Django's built-in template tag
-and everything should work.
+For now, sprinkle the token into your forms with Django's built-in template tag and everything should work:
{{< web >}}
```django
@@ -312,18 +158,7 @@ and everything should work.
```
{{< /book >}}
-That's how a form gets displayed.
-Now let's look at a view that handles the form properly.
-When working with form views,
-we will often use a view
-that is able to handle
-both `GET` and `POST` HTTP requests.
-Here's a full view
-that we can break down piece by piece.
-The example uses a function view
-for simplicity,
-but you could do something similar
-with a class-based view.
+That's how a form gets displayed. Now let's look at a view that handles the form properly. When working with form views, we will often use a view that is able to handle both `GET` and `POST` HTTP requests. Here's a full view that we can break down piece by piece. The example uses a function view for simplicity, but you could do something similar with a class-based view:
```python
# application/views.py
@@ -353,83 +188,32 @@ def contact_us(request):
)
```
-If we start
-by thinking about the `else` branch,
-we can see how little this view does
-on a `GET` request.
-When the HTTP method is a `GET`,
-it creates an empty form
-with no data passed to the constructor
-and renders a template
-with the `form`
-in the context.
-
-`contact_form.html` contains the Django template above
-to display the HTML form
-to the user.
-When the user clicks "Send the form!",
-another request comes
-to the same view,
-but this time the method is an HTTP `POST`
-and contains the submitted data.
-
-The `POST` request creates a `form`,
-but there is a difference
-in how it is constructed.
-The form submission data is stored
-in `request.POST`,
-which is a dictionary-like object
-that we first encountered
+If we start by thinking about the `else` branch, we can see how little this view does on a `GET` request. When the HTTP method is a `GET`, it creates an empty form with no data passed to the constructor and renders a template with the `form` in the context.
+
+`contact_form.html` contains the Django template above to display the HTML form to the user. When the user clicks "Send the form!", another request comes to the same view, but this time the method is an HTTP `POST` and contains the submitted data.
+
+The `POST` request creates a `form`, but there is a difference in how it is constructed. The form submission data is stored in `request.POST`, which is a dictionary-like object that we first encountered
{{< web >}}
in the views article.
{{< /web >}}
{{< book >}}
in the views chapter.
{{< /book >}}
-By passing `request.POST`
-to the form's constructor,
-we create a form with data.
-In the Django documentation,
-you will see this called a *bound form*
-because data is *bound* to the form.
-
-With the form ready,
-the view checks if the data is valid.
-We'll talk about form validation
-in detail later
+By passing `request.POST` to the form's constructor, we create a form with data. In the Django documentation, you will see this called a *bound form* because data is *bound* to the form.
+
+With the form ready, the view checks if the data is valid. We'll talk about form validation in detail later
{{< web >}}
in this article.
{{< /web >}}
{{< book >}}
in this chapter.
{{< /book >}}
-In this instance,
-you can see that `is_valid` could return `False`
-if the form data contained "I am not an email address"
-in the `email` field, for instance.
-
-* When the form is valid,
- the view does the extra work represented
- by the comment
- and redirects to a new view
- that can show some kind of success message.
-* When the form is invalid,
- the view goes out of the `if` clause
- and calls `render`.
- Since the data is bound to the `form`,
- the contact form has enough information
- to show which form fields
- caused the errors
- that made the form invalid.
-
-That's the core of form handling!
-The presented view is a common pattern
-for handling form views
-in Django.
-In fact, this view pattern is so common
-that Django provides a built-in view
-to implement what is done in the example
-named `FormView`.
+In this instance, you can see that `is_valid` could return `False` if the form data contained "I am not an email address" in the `email` field, for instance.
+
+* When the form is valid, the view does the extra work represented by the comment and redirects to a new view that can show some kind of success message.
+* When the form is invalid, the view goes out of the `if` clause and calls `render`. Since the data is bound to the `form`, the contact form has enough information to show which form fields caused the errors that made the form invalid.
+
+That's the core of form handling! The presented view is a common pattern for handling form views in Django. In fact, this view pattern is so common that Django provides a built-in view to implement what is done in the example named `FormView`:
```python
# application/views.py
@@ -454,22 +238,11 @@ class ContactUs(FormView):
return super().form_valid(form)
```
-The `FormView` expects a form class and template name
-and provides some methods to override
-for the common places
-where your own application logic should live.
+The `FormView` expects a form class and template name and provides some methods to override for the common places where your own application logic should live.
## Form Fields
-With the basics of form handling done,
-we can turn our attention
-to the kinds
-of fields
-that forms can use.
-The extensive list of fields is
-in {{< extlink "https://docs.djangoproject.com/en/4.1/ref/forms/fields/" "the Django documentation" >}},
-and we will look at a few
-of the most common ones
+With the basics of form handling done, we can turn our attention to the kinds of fields that forms can use. The extensive list of fields is in {{< extlink "https://docs.djangoproject.com/en/4.1/ref/forms/fields/" "the Django documentation" >}}, and we will look at a few of the most common ones
{{< web >}}
in this article.
{{< /web >}}
@@ -477,62 +250,17 @@ in this article.
in this chapter.
{{< /book >}}
-The first thing to remember about Django form fields is
-that they convert HTML form data
-into native Python data types.
-If you examine the data
-of a form submission,
-you'll discover
-that each value is essentially a string
-by default.
-If Django did nothing for you,
-then you would constantly have to convert
-to the data types that you want.
-By working with form fields,
-that data conversion is automatically handled
-for you.
-For instance,
-if you choose a `BooleanField`,
-after the Django form is validated,
-that field value will be either `True` or `False`.
-
-Another important item to know about fields is
-that they are associated
-with particular Django widgets.
-Widgets are the way
-to control what Django renders
-when you render a form.
-Each form field has a default widget type.
-Sticking with `BooleanField`,
-its default widget is a `CheckboxInput`
-which will render an `input` tag
-with a type of `checkbox`
-(i.e., your standard form checkbox).
-
-Fields are the critical intersection
-between the world of the browser and HTML
-and the Python world
-with all of its robust data types.
-
-What fields are you most likely
-to reach for?
-And what do you need to set on those fields?
+The first thing to remember about Django form fields is that they convert HTML form data into native Python data types. If you examine the data of a form submission, you'll discover that each value is essentially a string by default. If Django did nothing for you, then you would constantly have to convert to the data types that you want. By working with form fields, that data conversion is automatically handled for you. For instance, if you choose a `BooleanField`, after the Django form is validated, that field value will be either `True` or `False`.
+
+Another important item to know about fields is that they are associated with particular Django widgets. Widgets are the way to control what Django renders when you render a form. Each form field has a default widget type. Sticking with `BooleanField`, its default widget is a `CheckboxInput` which will render an `input` tag with a type of `checkbox` (i.e., your standard form checkbox).
+
+Fields are the critical intersection between the world of the browser and HTML and the Python world with all of its robust data types.
+
+What fields are you most likely to reach for? And what do you need to set on those fields?
### CharField
-`CharField` is a real workhorse
-for Django forms.
-The `CharField` captures text input
-and uses a standard `input` tag
-with a type of `text`.
-If you want to collect more text,
-like in a feedback form,
-you can switch
-from the default `TextInput` widget
-to a `Textarea` widget.
-This will make your form render a `textarea` tag
-that will give far more space
-for any input.
+`CharField` is a real workhorse for Django forms. The `CharField` captures text input and uses a standard `input` tag with a type of `text`. If you want to collect more text, like in a feedback form, you can switch from the default `TextInput` widget to a `Textarea` widget. This will make your form render a `textarea` tag that will give far more space for any input:
```python
# application/forms.py
@@ -548,40 +276,16 @@ class FeedbackForm(forms.Form):
### EmailField
-The `EmailField` is like a specialized version
-of the `CharField`.
-The field uses an `input` tag
-with a type of `email`.
-Many modern browsers can help
-to check that valid email addresses are provided.
-Also, when this field is validated within the framework,
-Django will attempt to validate the email address too
-in case the browser wasn't able to do it.
+The `EmailField` is like a specialized version of the `CharField`. The field uses an `input` tag with a type of `email`. Many modern browsers can help to check that valid email addresses are provided. Also, when this field is validated within the framework, Django will attempt to validate the email address too in case the browser wasn't able to do it.
### DateField
-A `DateField` is another field
-that is mostly like a `CharField`.
-The field even uses the `input` type of `text`
-when rendered.
-The difference with this field comes
-from the data type that the form will provide
-after it is validated.
-A `DateField` will convert
-{{< extlink "https://docs.djangoproject.com/en/4.1/ref/settings/#datetime-input-formats" "a variety of string formats" >}}
+A `DateField` is another field that is mostly like a `CharField`. The field even uses the `input` type of `text` when rendered. The difference with this field comes from the data type that the form will provide after it is validated. A `DateField` will convert {{< extlink "https://docs.djangoproject.com/en/4.1/ref/settings/#datetime-input-formats" "a variety of string formats" >}}
into a Python `datetime.date` object.
### ChoiceField
-A `ChoiceField` is useful
-when you want a user to make a choice
-from a list of options.
-For this field type,
-we must provide a list of choices
-that the user can pick from.
-Imagine that we want to ask users
-what their favorite meal of the day is.
-Here's a form that can do that.
+A `ChoiceField` is useful when you want a user to make a choice from a list of options. For this field type, we must provide a list of choices that the user can pick from. Imagine that we want to ask users what their favorite meal of the day is. Here's a form that can do that:
```python
# application/forms.py
@@ -599,9 +303,7 @@ class SurveyForm(forms.Form):
)
```
-This will contain a form
-with a `select` tag
-that looks like:
+This will contain a form with a `select` tag that looks like:
```html
@@ -614,96 +316,29 @@ that looks like:
```
-This handful of fields will deal
-with most form needs.
-Be sure to explore the full list
-of what is available
-to equip yourself
-with other beneficial types.
-
-Form fields share some common attributes
-for things that each field needs.
-
-The `required` attribute is a boolean
-that specifies whether a field must have a value or not.
-For instance,
-it wouldn't make much sense
-if your site had a support form
-that you planned to use to contact people via email,
-and an `email` field
-on the form
-was optional.
-
-`label` sets what text is used
-for the `label` tag
-that is rendered
-with a form `input`.
-In the meal example,
-we could use the `label` attribute to change "Favorite meal"
-into "What is your favorite meal?"
-That would make a much better survey experience.
-
-Sometimes forms may not be clear
-and users need help.
-You can add a `help_text` attribute
-that will render additional text
-by your form field wrapped in a `span` tag
-with a `helptext` class
-if you want to style it with CSS.
-
-Because forms are one of the main ways
-that users will provide information
-to your application,
-the forms system is rich with features.
-You can learn a lot about Django
-by diving deeply
-into that portion
-of the documentation.
-
-Let's shift our focus
-to form validation
-since I've mentioned it a few times
-in passing now.
+This handful of fields will deal with most form needs. Be sure to explore the full list of what is available to equip yourself with other beneficial types.
+
+Form fields share some common attributes for things that each field needs.
+
+The `required` attribute is a boolean that specifies whether a field must have a value or not. For instance, it wouldn't make much sense if your site had a support form that you planned to use to contact people via email, and an `email` field on the form was optional.
+
+`label` sets what text is used for the `label` tag that is rendered with a form `input`. In the meal example, we could use the `label` attribute to change "Favorite meal" into "What is your favorite meal?" That would make a much better survey experience.
+
+Sometimes forms may not be clear and users need help. You can add a `help_text` attribute that will render additional text by your form field wrapped in a `span` tag with a `helptext` class if you want to style it with CSS.
+
+Because forms are one of the main ways that users will provide information to your application, the forms system is rich with features. You can learn a lot about Django by diving deeply into that portion of the documentation.
+
+Let's shift our focus to form validation since I've mentioned it a few times in passing now.
## Validating Forms
-In the view example,
-I showed a form
-that calls the `is_valid` method.
-At a high level,
-we can understand
-what that method is doing;
-it's determining whether the form is valid or not.
-
-But what is `is_valid` actually doing?
-It does a lot!
-
-The method handles each of the fields.
-As we saw,
-fields can have a final data type
-(like with `BooleanField`)
-or an expected structure
-(like with `EmailField`).
-This process of converting data types
-and validating the field data is called cleaning.
-In fact,
-each field must have a `clean` method
-that the form will call
-when `is_valid` is called.
-
-When `is_valid` is `True`,
-the form's data will be
-in a dictionary named `cleaned_data`
-with keys
-that match the field names
-declared by the form.
-With the validated data,
-you access `cleaned_data` to do your work.
-For instance,
-if we had some kind of integration
-with a support ticket system,
-perhaps our `FeedbackForm` above is handled
-in the view like:
+In the view example, I showed a form that calls the `is_valid` method. At a high level, we can understand what that method is doing; it's determining whether the form is valid or not.
+
+But what is `is_valid` actually doing? It does a lot!
+
+The method handles each of the fields. As we saw, fields can have a final data type (like with `BooleanField`) or an expected structure (like with `EmailField`). This process of converting data types and validating the field data is called cleaning. In fact, each field must have a `clean` method that the form will call when `is_valid` is called.
+
+When `is_valid` is `True`, the form's data will be in a dictionary named `cleaned_data` with keys that match the field names declared by the form. With the validated data, you access `cleaned_data` to do your work. For instance, if we had some kind of integration with a support ticket system, perhaps our `FeedbackForm` above is handled in the view like:
```python
if form.is_valid():
@@ -718,36 +353,11 @@ if form.is_valid():
)
```
-When `is_valid` is `False`,
-Django will store the errors it found
-in an `errors` attribute.
-This attribute will be used
-when the form is re-rendered
-on the page
-(because, if you recall from the view example,
-the form view pattern sends a bound form
-back through a `render` call
-in the failure case).
-
-Once again,
-Django is doing a lot of heavy lifting
-for you
-to make working with forms easier.
-The system *also* permits developers
-to add custom validation logic.
-
-If you have a form field,
-you can add customization
-by writing a method on the form class.
-The format of the method must match with the field name
-and prepend `clean_`.
-Let's imagine that we want a website
-for Bobs.
-In order to sign up for the website,
-your email address must have "bob"
-in it.
-We can write a clean method
-to check for that.
+When `is_valid` is `False`, Django will store the errors it found in an `errors` attribute. This attribute will be used when the form is re-rendered on the page (because, if you recall from the view example, the form view pattern sends a bound form back through a `render` call in the failure case).
+
+Once again, Django is doing a lot of heavy lifting for you to make working with forms easier. The system *also* permits developers to add custom validation logic.
+
+If you have a form field, you can add customization by writing a method on the form class. The format of the method must match with the field name and prepend `clean_`. Let's imagine that we want a website for Bobs. In order to sign up for the website, your email address must have "bob" in it. We can write a clean method to check for that:
```python
# application/forms.py
@@ -772,31 +382,10 @@ class SignUpForm(forms.Form):
There are a few important points about this:
* `clean_email` will only try to clean the `email` field.
-* If validation fails,
- your code should raise a `ValidationError`.
- Django will handle that and put the error
- in the right format
- in the `errors` attribute
- of the form.
-* If everything is good,
- be sure to return the cleaned data.
- That is part of the interface
- that Django expects for clean methods.
-
-These `clean_` methods are hooks
-that let you include extra checking.
-This hook system gives you the perfect place
-to put validation logic for data
-that is specific
-to your application.
-But what about validating multiple pieces
-of data?
-This might happen when data has some kind
-of interrelationship.
-For instance,
-if you're putting together a genealogy website,
-you may have a form that records birth and death dates.
-You might want to check those dates.
+* If validation fails, your code should raise a `ValidationError`. Django will handle that and put the error in the right format in the `errors` attribute of the form.
+* If everything is good, be sure to return the cleaned data. That is part of the interface that Django expects for clean methods.
+
+These `clean_` methods are hooks that let you include extra checking. This hook system gives you the perfect place to put validation logic for data that is specific to your application. But what about validating multiple pieces of data? This might happen when data has some kind of interrelationship. For instance, if you're putting together a genealogy website, you may have a form that records birth and death dates. You might want to check those dates:
```python
# application/forms.py
@@ -823,27 +412,13 @@ class HistoricalPersonForm(forms.Form):
return cleaned_data
```
-This method is similar to `clean_`,
-but we must be more careful.
-Individual field validations run first,
-but they may have failed!
-When clean methods fail,
-the form field is removed from `cleaned_data`
-so we can't do a direct key access.
-The clean method checks if the two dates are truthy and each has a value,
-then does the comparison between them.
+This method is similar to `clean_`, but we must be more careful. Individual field validations run first, but they may have failed! When clean methods fail, the form field is removed from `cleaned_data` so we can't do a direct key access. The clean method checks if the two dates are truthy and each has a value, then does the comparison between them.
-Custom validation is a great feature to improve the quality
-of the data you collect
-from users of your application.
+Custom validation is a great feature to improve the quality of the data you collect from users of your application.
## Summary
-That's how forms make it possible
-to collect data
-from your users
-so your site can interact with them.
-We've seen:
+That's how forms make it possible to collect data from your users so your site can interact with them. We've seen:
* Web forms and the `form` HTML tag
* The `Form` class that Django uses to handle form data in Python
@@ -851,37 +426,22 @@ We've seen:
* Controlling what fields are in forms
* How to do form validation
-Now that we know how to collect data
-from users,
-how can we make the application hold onto that data
-for them?
+Now that we know how to collect data from users, how can we make the application hold onto that data for them?
{{< web >}}
In the next article,
{{< /web >}}
{{< book >}}
In the next chapter,
{{< /book >}}
-we will begin to store data
-in a database.
-We'll work with:
+we will begin to store data in a database. We'll work with:
* How to set up a database for your project.
* How Django uses special classes called models to keep data.
-* Running the commands that will prepare a database
- for the models you want to use.
+* Running the commands that will prepare a database for the models you want to use.
* Saving new information into the database.
* Asking the database for information that we stored.
{{< web >}}
-If you'd like to follow along
-with the series,
-please feel free to sign up
-for my newsletter
-where I announce all of my new content.
-If you have other questions,
-you can reach me online
-on Twitter
-where I am
-{{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
+If you'd like to follow along with the series, please feel free to sign up for my newsletter where I announce all of my new content. If you have other questions, you can reach me online on Twitter where I am {{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
{{< /web >}}
From ada204eebf24a1d9ab57b5aad431838129345540 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nazar=C3=A9=20da=20Piedade?=
Date: Fri, 5 May 2023 11:10:31 +0100
Subject: [PATCH 07/30] chore: format
`understand-django/2020-06-25-store-data-with-models.pt.md`
---
.../2020-06-25-store-data-with-models.pt.md | 947 +++---------------
1 file changed, 142 insertions(+), 805 deletions(-)
diff --git a/content/understand-django/2020-06-25-store-data-with-models.pt.md b/content/understand-django/2020-06-25-store-data-with-models.pt.md
index de4afbbf..8a312063 100644
--- a/content/understand-django/2020-06-25-store-data-with-models.pt.md
+++ b/content/understand-django/2020-06-25-store-data-with-models.pt.md
@@ -1,14 +1,7 @@
---
title: "Store Data With Models"
description: >-
- In this article,
- we will see how to store data
- into a database
- with Django models.
- The article covers how models act
- as an interface
- to let your application
- store and fetch data.
+ In this article, we will see how to store data into a database with Django models. The article covers how models act as an interface to let your application store and fetch data.
image: img/django.png
type: post
categories:
@@ -22,55 +15,20 @@ tags:
---
{{< web >}}
-In the previous
-[Understand Django]({{< ref "/understand-django/_index.md" >}})
-article,
-we encountered forms
-and how forms allow your application
-to receive data
-from users
-who use your site.
-In this article,
-you'll see how to take
-that data
+In the previous [Understand Django]({{< ref "/understand-django/_index.md" >}}) article, we encountered forms and how forms allow your application to receive data from users who use your site. In this article, you'll see how to take that data
{{< /web >}}
{{< book >}}
-In this chapter,
-you'll see how to take data
+In this chapter, you'll see how to take data
{{< /book >}}
-and store it
-into a database
-so that your application can use that data
-or display it later.
+and store it into a database so that your application can use that data or display it later.
-{{< understand-django-series "models" >}}
+{{< understand-django-series-pt "models" >}}
## Setting Up
-Let's figure out where your data goes
-before getting deep
-into how to work with it.
-Django uses databases
-to store data.
-More specifically,
-Django uses *relational* databases.
-Covering relational databases would be a gigantic topic
-so you'll have to settle
-for a **very** abridged version.
-
-A relational database is like a collection
-of spreadsheets.
-Each spreadsheet is actually called a table.
-A table has a set of columns
-to track different pieces
-of data.
-Each row in the table would represent a related group.
-For instance,
-imagine we have an employee table
-for a company.
-The columns for an employee table might include a first name, last name,
-and job title.
-Each row would represent an individual employee.
+Let's figure out where your data goes before getting deep into how to work with it. Django uses databases to store data. More specifically, Django uses *relational* databases. Covering relational databases would be a gigantic topic so you'll have to settle for a **very** abridged version.
+
+A relational database is like a collection of spreadsheets. Each spreadsheet is actually called a table. A table has a set of columns to track different pieces of data. Each row in the table would represent a related group. For instance, imagine we have an employee table for a company. The columns for an employee table might include a first name, last name, and job title. Each row would represent an individual employee:
```text
First name | Last name | Job title
@@ -80,34 +38,11 @@ John | Smith | Software Engineer
Peggy | Jones | Software Engineer
```
-The "relational" part of a relational database comes into play
-because multiple tables can *relate*
-to each other.
-In our company example,
-the database could have a table of phone numbers
-that it uses to store the phone number
-of each employee.
-
-Why not put the phone number in the same employee table?
-Well,
-what would happen if a company needed a cell phone number
-and home phone number?
-By having separate tables,
-we could support tracking multiple phone number types.
-There is a lot of power that comes
-from being able to separate these different kinds
-of data.
-We'll see the power of relational databases
-as we explore how Django exposes
-that power.
-
-Django uses a relational database
-so the framework must have some ability
-to set up that database.
-The database configuration is in the `DATABASES` setting
-in your `settings.py` file.
-After running `startproject`,
-you'll find:
+The "relational" part of a relational database comes into play because multiple tables can *relate* to each other. In our company example, the database could have a table of phone numbers that it uses to store the phone number of each employee.
+
+Why not put the phone number in the same employee table? Well, what would happen if a company needed a cell phone number and home phone number? By having separate tables, we could support tracking multiple phone number types. There is a lot of power that comes from being able to separate these different kinds of data. We'll see the power of relational databases as we explore how Django exposes that power.
+
+Django uses a relational database so the framework must have some ability to set up that database. The database configuration is in the `DATABASES` setting in your `settings.py` file. After running `startproject`, you'll find:
```python
DATABASES = {
@@ -118,52 +53,11 @@ DATABASES = {
}
```
-Like we saw with the templates system,
-Django supports multiple databases.
-Unlike the template system,
-the database settings refer to each supported backend as an "engine"
-instead of "backend."
-The default database engine
-from `startproject`
-is set to use
-{{< extlink "https://www.sqlite.org/index.html" "SQLite" >}}.
-SQLite is a great starting choice
-because it fits an entire relational database
-into a single file
-which the settings name `db.sqlite3`.
-This choice of engine really lowers the barrier
-to starting
-with Django
-since new Django developers don't have to go download additional tools
-to try Django out.
-
-SQLite is an amazing little database
-and probably the mostly widely used database
-in the world.
-The database exists in every smartphone you could think of.
-Even though SQLite is amazing,
-it's not a good fit for many scenarios
-where you'd want to use Django.
-For starters,
-the database only permits one user
-to write to it
-at a time.
-That's a huge problem if you're planning
-to make a site that serves many people simultaneously.
-
-Because SQLite is not the best fit
-for an application on the web,
-you'll likely need to switch
-to a different relational database.
-I'd recommend {{< extlink "https://www.postgresql.org/" "PostgreSQL" >}}.
-Postgres (as it is often "abbreviated" to) is a wildly popular,
-open source database
-that is very well supported.
-Combined with {{< extlink "https://www.psycopg.org/docs/" "psycopg2" >}}
-as the Django engine,
-you'll find that many places
-that can host your Django app will work well
-with Postgres.
+Like we saw with the templates system, Django supports multiple databases. Unlike the template system, the database settings refer to each supported backend as an "engine" instead of "backend." The default database engine from `startproject` is set to use {{< extlink "https://www.sqlite.org/index.html" "SQLite" >}}. SQLite is a great starting choice because it fits an entire relational database into a single file which the settings name `db.sqlite3`. This choice of engine really lowers the barrier to starting with Django since new Django developers don't have to go download additional tools to try Django out.
+
+SQLite is an amazing little database and probably the mostly widely used database in the world. The database exists in every smartphone you could think of. Even though SQLite is amazing, it's not a good fit for many scenarios where you'd want to use Django. For starters, the database only permits one user to write to it at a time. That's a huge problem if you're planning to make a site that serves many people simultaneously.
+
+Because SQLite is not the best fit for an application on the web, you'll likely need to switch to a different relational database. I'd recommend {{< extlink "https://www.postgresql.org/" "PostgreSQL" >}}. Postgres (as it is often "abbreviated" to) is a wildly popular, open source database that is very well supported. Combined with {{< extlink "https://www.psycopg.org/docs/" "psycopg2" >}} as the Django engine, you'll find that many places that can host your Django app will work well with Postgres.
We can explore more database configuration
{{< web >}}
@@ -172,46 +66,20 @@ in a future article
{{< book >}}
in a future chapter
{{< /book >}}
-on deployment.
-For now,
-while you're learning,
-SQLite is perfectly well suited
-for the task.
+on deployment. For now, while you're learning, SQLite is perfectly well suited for the task.
## Modeling Your Data
-Now that you have an idea
-of where Django will store your data,
-let's focus on *how* Django will store data.
-
-Django represents data
-for a database
-in Python classes
-called **models**.
-Django models are similar
-to the form classes
-that we saw
+Now that you have an idea of where Django will store your data, let's focus on *how* Django will store data.
+
+Django represents data for a database in Python classes called **models**. Django models are similar to the form classes that we saw
{{< web >}}
in the last article.
{{< /web >}}
{{< book >}}
in the last chapter.
{{< /book >}}
-A Django model declares the data
-that you want to store
-in the database
-as class level attributes,
-just like a form class.
-In fact,
-the types of fields are extremely similar
-to their form counterparts,
-and for good reason!
-We often want to save form data
-and store it
-so it makes sense for models
-to be similar to forms
-in structure.
-Let's look at an example.
+A Django model declares the data that you want to store in the database as class level attributes, just like a form class. In fact, the types of fields are extremely similar to their form counterparts, and for good reason! We often want to save form data and store it so it makes sense for models to be similar to forms in structure. Let's look at an example:
```python
# application/models.py
@@ -229,21 +97,7 @@ class Employee(models.Model):
)
```
-This model class describes the pieces
-that we want to include
-in a database table.
-Each model class represents one database table.
-If we wanted the phone numbers
-that I mentioned earlier,
-we'd create a separate `PhoneNumber` class.
-Conventionally,
-we use a singular name
-instead of a plural
-when naming the class.
-We do that because each *row*
-in the table
-is represented
-as an object instance.
+This model class describes the pieces that we want to include in a database table. Each model class represents one database table. If we wanted the phone numbers that I mentioned earlier, we'd create a separate `PhoneNumber` class. Conventionally, we use a singular name instead of a plural when naming the class. We do that because each *row* in the table is represented as an object instance:
```python
>>> from application.models import Employee
@@ -255,81 +109,24 @@ as an object instance.
'Tom'
```
-This example appears
-to create a new employee,
-but it's missing a key element.
-We haven't saved the `employee` instance
-to the database.
-We can do that with `employee.save()`,
-but,
-if you are following along
-and try to call that right now,
-it will fail
-with an error that says
-that the employee table doesn't exist.
-
-Since the database is a tool
-that is external to Django,
-the database needs a bit of preparation
-before it can receive data
-from Django.
+This example appears to create a new employee, but it's missing a key element. We haven't saved the `employee` instance to the database. We can do that with `employee.save()`, but, if you are following along and try to call that right now, it will fail with an error that says that the employee table doesn't exist.
+
+Since the database is a tool that is external to Django, the database needs a bit of preparation before it can receive data from Django.
## Preparing A Database With Migrations
-We now know that Django models are Python classes
-that map to database tables.
-Database tables don't magically appear.
-We need the ability to set up tables
-so that they will match the structure
-defined in the Python class.
-The tool Django provides
-to make Django models
-and a database sync up
-is called the migrations system.
-
-Migrations are Python files
-that describe the sequence
-of database operations
-that are needed
-to make a database match any model definitions
-that you have in your project.
-
-Because Django works
-with many databases,
-these database operations are defined
-in Python files
-so that the operations can be abstract.
-By using abstract operations,
-the Django migration system can plug in the specific database commands
-for whatever database you're using.
-If you're starting out with SQLite,
-then moving to PostgreSQL
-when you're ready
-to put your application
-on the internet,
-then the migration system will do its best
-to smooth over the differences
-to minimize the amount
-of work you would need
-while making the transition.
-
-Initially,
-you can get pretty far
-without understanding the internals
-of how migration files work.
-At the core level,
-you need to learn a couple
-of Django commands:
-`makemigrations`
+We now know that Django models are Python classes that map to database tables. Database tables don't magically appear. We need the ability to set up tables so that they will match the structure defined in the Python class. The tool Django provides to make Django models and a database sync up is called the migrations system.
+
+Migrations are Python files that describe the sequence of database operations that are needed to make a database match any model definitions that you have in your project.
+
+Because Django works with many databases, these database operations are defined in Python files so that the operations can be abstract. By using abstract operations, the Django migration system can plug in the specific database commands for whatever database you're using. If you're starting out with SQLite, then moving to PostgreSQL when you're ready to put your application on the internet, then the migration system will do its best to smooth over the differences to minimize the amount of work you would need while making the transition.
+
+Initially, you can get pretty far without understanding the internals of how migration files work. At the core level, you need to learn a couple of Django commands: `makemigrations`
and `migrate`.
### `makemigrations`
-The `makemigrations` command will create any migration files
-if there are any pending model changes.
-To create our migration file
-for the `Employee` model,
-we can run:
+The `makemigrations` command will create any migration files if there are any pending model changes. To create our migration file for the `Employee` model, we can run:
```bash
(venv) $ ./manage.py makemigrations
@@ -338,36 +135,18 @@ Migrations for 'application':
- Create model Employee
```
-The important thing to note is that we require a new migration
-when we make model changes
-that update any model fields.
-This includes:
+The important thing to note is that we require a new migration when we make model changes that update any model fields. This includes:
* Adding new models or new fields
* Modifying existing fields
* Deleting existing fields
* Changing some model metadata and a few other edge cases
-If you do not make a migration,
-you will likely encounter errors
-when fetching data
-from the database.
-This is because Django only builds queries
-based on what is defined
-in the Python code.
-The system assumes
-that the database is in the proper state.
-Django will try to query
-on database tables
-even if those tables don't exist yet!
+If you do not make a migration, you will likely encounter errors when fetching data from the database. This is because Django only builds queries based on what is defined in the Python code. The system assumes that the database is in the proper state. Django will try to query on database tables even if those tables don't exist yet!
### `migrate`
-The other command, `migrate`,
-takes migration files
-and applies them
-to a database.
-For example:
+The other command, `migrate`, takes migration files and applies them to a database. For example:
```bash
(venv) $ ./manage.py migrate
@@ -394,81 +173,29 @@ Running migrations:
Applying sessions.0001_initial... OK
```
-This is the output that happens
-when applying our new migration.
-What is all this stuff!?
-
-The migration system is also used
-by built-in Django applications.
-In my sample project,
-I used `startproject`
-which includes a set
-of common applications
-in the `INSTALLED_APPS` list.
-We can observe that our sample `application` applied its migration,
-and the migrations
-from the other Django applications
-that we included are also applied.
-
-If you run the `migrate` command again,
-you won't see the same output.
-That's because Django keeps tracks
-of which migrations were applied.
-The migration system will only execute any *unapplied* migrations.
-
-You can also limit which migrations to execute
-by providing a Django app name.
+This is the output that happens when applying our new migration. What is all this stuff!?
+
+The migration system is also used by built-in Django applications. In my sample project, I used `startproject` which includes a set of common applications in the `INSTALLED_APPS` list. We can observe that our sample `application` applied its migration, and the migrations from the other Django applications that we included are also applied.
+
+If you run the `migrate` command again, you won't see the same output. That's because Django keeps tracks of which migrations were applied. The migration system will only execute any *unapplied* migrations.
+
+You can also limit which migrations to execute by providing a Django app name:
```bash
(venv) $ ./manage.py migrate application
```
-Those are the fundamentals
-of migrations.
-You can also use migrations
-to apply more complex operations
-like actions
-that are specific
-to your selected database.
-You can learn more
-about what Django migrations can do
-in the {{< extlink "https://docs.djangoproject.com/en/4.1/topics/migrations/" "Migrations documentation" >}}.
+Those are the fundamentals of migrations. You can also use migrations to apply more complex operations like actions that are specific to your selected database. You can learn more about what Django migrations can do in the {{< extlink "https://docs.djangoproject.com/en/4.1/topics/migrations/" "Migrations documentation" >}}.
## Working With Models
-After running migrations,
-your database will be prepared
-to communicate properly
-with Django.
-
-To create new rows
-in our new database tables,
-we can use a model's `save` method.
-When you save a model instance,
-Django will send a message
-to the database
-that effectively says
-"add this new data
-to this database table."
-These database "messages"
-are actually called **queries**.
-
-As I mentioned
-in the settings section,
-Django communicates
-with the database
-via a database engine.
-The database engine uses Structured Query Language (SQL)
-to communicate
-with the actual database.
-SQL is the common standard
-that Django's supported databases all "speak."
-Since Django uses SQL,
-that's why a message is named "query."
-
-What does an SQL query look like?
-If we save our model example from earlier,
-it would look something like:
+After running migrations, your database will be prepared to communicate properly with Django.
+
+To create new rows in our new database tables, we can use a model's `save` method. When you save a model instance, Django will send a message to the database that effectively says "add this new data to this database table." These database "messages" are actually called **queries**.
+
+As I mentioned in the settings section, Django communicates with the database via a database engine. The database engine uses Structured Query Language (SQL) to communicate with the actual database. SQL is the common standard that Django's supported databases all "speak." Since Django uses SQL, that's why a message is named "query."
+
+What does an SQL query look like? If we save our model example from earlier, it would look something like:
```sql
INSERT INTO "application_employee"
@@ -477,70 +204,29 @@ VALUES
('Tom', 'Bombadil', 'Old Forest keeper')
```
-Note that this example is an `INSERT` query
-when using the SQLite engine.
-Like natural languages,
-SQL has a variety of dialects
-depending on the database
-that you select.
-The databases do their best to adhere
-to some standards,
-but each database has its quirks,
-and the job
-of the database engine is
-to even out those differences
-where possible.
-
-This is another area where Django does a ton
-of hard work
-on your behalf.
-The database engine translates your `save` call
-into the proper SQL query.
-SQL is a deep topic
-that we can't possibly cover fully
+Note that this example is an `INSERT` query when using the SQLite engine. Like natural languages, SQL has a variety of dialects depending on the database that you select. The databases do their best to adhere to some standards, but each database has its quirks, and the job of the database engine is to even out those differences where possible.
+
+This is another area where Django does a ton of hard work on your behalf. The database engine translates your `save` call into the proper SQL query. SQL is a deep topic that we can't possibly cover fully
{{< web >}}
in this article.
{{< /web >}}
{{< book >}}
in this chapter.
{{< /book >}}
-Thankfully,
-we don't have to
-because of Django's ORM!
-
-ORM stands for Object Relational Mapper.
-The job of an ORM
-is to map (or translate)
-from Python *objects*
-to a *relational* database.
-This means that we spend our time working
-in Python code,
-and we let Django figure out how
-to get and put data
-into the database.
-
-Using `save` on a model record is such a small example
-of the Django ORM.
-What else can we do?
-We can do things like:
+Thankfully, we don't have to because of Django's ORM!
+
+ORM stands for Object Relational Mapper. The job of an ORM is to map (or translate) from Python *objects* to a *relational* database. This means that we spend our time working in Python code, and we let Django figure out how to get and put data into the database.
+
+Using `save` on a model record is such a small example of the Django ORM. What else can we do? We can do things like:
* Get all rows from the database.
* Get a filtered set of rows based on some filtering criteria.
* Update a set of rows at the same time.
* Delete rows from the database.
-Most of these operations
-with the Django ORM work
-through a `Manager` class.
-Where our previous example showed how
-to manipulate a single row,
-a model manager has methods designed
-for interacting with multiple rows.
+Most of these operations with the Django ORM work through a `Manager` class. Where our previous example showed how to manipulate a single row, a model manager has methods designed for interacting with multiple rows.
-We can analyze our fictitious employee table.
-The manager for a model is attached to the model class
-as an attribute named `objects`.
-Let's see some code:
+We can analyze our fictitious employee table. The manager for a model is attached to the model class as an attribute named `objects`. Let's see some code:
```python
>>> from application.models import Employee
@@ -562,24 +248,9 @@ FROM "application_employee"
WHERE "application_employee"."first_name" = Bob
```
-In this example,
-we're using the manager
-to filter to a subset
-of employees
-within the table.
-The `bobs` variable returned
-by the `filter` method
-is a `QuerySet`.
-As you might guess,
-it represents a set of rows
-that an SQL query will return.
-Whenever you have a queryset,
-you can print the query
-to see the exact SQL statement
-that Django will run
-on your behalf.
-
-What if you want to delete an employee record?
+In this example, we're using the manager to filter to a subset of employees within the table. The `bobs` variable returned by the `filter` method is a `QuerySet`. As you might guess, it represents a set of rows that an SQL query will return. Whenever you have a queryset, you can print the query to see the exact SQL statement that Django will run on your behalf.
+
+What if you want to delete an employee record?:
```python
>>> from application.models import Employee
@@ -590,22 +261,9 @@ What if you want to delete an employee record?
(1, {'application.Employee': 1})
```
-A queryset can apply operations in bulk.
-In this case,
-the filter is sufficiently narrow
-that only one record was deleted,
-but it could have included more
-if the SQL query matched more database table rows.
-
-The `QuerySet` class has a variety
-of methods
-that are useful
-when working with tables.
-Some of the methods also have the interesting property
-of returning a new queryset.
-This is a beneficial capability
-when you need to apply additional logic
-for your query.
+A queryset can apply operations in bulk. In this case, the filter is sufficiently narrow that only one record was deleted, but it could have included more if the SQL query matched more database table rows.
+
+The `QuerySet` class has a variety of methods that are useful when working with tables. Some of the methods also have the interesting property of returning a new queryset. This is a beneficial capability when you need to apply additional logic for your query:
```python
from application.models import Employee
@@ -620,12 +278,9 @@ if should_find_the_bobs:
)
```
-Here are some other `QuerySet` methods
-that I use constantly:
+Here are some other `QuerySet` methods that I use constantly:
-* `create` - As an alternative to creating a record instance
- and calling `save`,
- the manager can create a record directly.
+* `create` - As an alternative to creating a record instance and calling `save`, the manager can create a record directly:
```python
Employee.objects.create(
@@ -634,11 +289,7 @@ Employee.objects.create(
)
```
-* `get` - Use this method when you want one
- *and exactly one* record.
- If your query doesn't match
- or will return multiple records,
- you'll get an exception.
+* `get` - Use this method when you want one *and exactly one* record. If your query doesn't match or will return multiple records, you'll get an exception:
```python
the_bob = Employee.objects.get(
@@ -657,8 +308,7 @@ Employee.objects.get(
# Raises application.models.Employee.DoesNotExist
```
-* `exclude` - This method lets you exclude rows
- that may be part of your existing queryset.
+* `exclude` - This method lets you exclude rows that may be part of your existing queryset:
```python
the_other_bobs = (
@@ -667,9 +317,7 @@ the_other_bobs = (
)
```
-* `update` - With this method,
- you can update a group of rows
- in a single operation.
+* `update` - With this method, you can update a group of rows in a single operation:
```python
Employee.objects.filter(
@@ -677,28 +325,21 @@ Employee.objects.filter(
).update(first_name='Robert')
```
-* `exists` - Use this method if you want to check
- if rows exist in the database
- that match the condition you want to check.
+* `exists` - Use this method if you want to check if rows exist in the database that match the condition you want to check:
```python
has_bobs = Employee.objects.filter(
first_name='Bob').exists()
```
-* `count` - Check how many rows match a condition.
- Because of how SQL works,
- note that this is more efficient
- than trying to use `len` on a queryset.
+* `count` - Check how many rows match a condition. Because of how SQL works, note that this is more efficient than trying to use `len` on a queryset:
```python
how_many_bobs = Employee.objects.filter(
first_name='Bob').count()
```
-* `none` - This returns an empty queryset for the model.
- How could this be useful?
- I use this when I need to protect certain data access.
+* `none` - This returns an empty queryset for the model. How could this be useful? I use this when I need to protect certain data access:
```python
employees = Employee.objects.all()
@@ -707,11 +348,7 @@ if not is_hr:
employees = Employee.objects.none()
```
-* `first` / `last` - These methods will return an individual model instance
- if one matches.
- The methods use ordering on the models
- to get the desired result.
- We use `order_by` to tell how we want the results arranged.
+* `first` / `last` - These methods will return an individual model instance if one matches. The methods use ordering on the models to get the desired result. We use `order_by` to tell how we want the results arranged:
```python
>>> a_bob = Employee.objects.filter(
@@ -721,36 +358,20 @@ if not is_hr:
Ross
```
-An `order_by` operation can also reverse the order of the results.
-To do this, add a dash before the field name
-like `order_by('-last_name')`.
+An `order_by` operation can also reverse the order of the results. To do this, add a dash before the field name like `order_by('-last_name')`.
-With the knowledge
-of how you can interact
-with models,
-we can focus more closely
-on what data can be stored
-in models
-(and, thus, in your database).
+With the knowledge of how you can interact with models, we can focus more closely on what data can be stored in models (and, thus, in your database).
## Types Of Model Data
-The `Employee` table that I've used
-as the example
+The `Employee` table that I've used as the example
{{< web >}}
for this article
{{< /web >}}
{{< book >}}
for this chapter
{{< /book >}}
-only has three `CharField` fields
-on the model.
-The choice was deliberate
-because I wanted you
-to have a chance
-to absorb a bit about the Django ORM
-and working with querysets
-before seeing other data types.
+only has three `CharField` fields on the model. The choice was deliberate because I wanted you to have a chance to absorb a bit about the Django ORM and working with querysets before seeing other data types.
{{< web >}}
We saw in the forms article
@@ -758,32 +379,11 @@ We saw in the forms article
{{< book >}}
We saw in the forms chapter
{{< /book >}}
-that Django's form system
-includes a wide variety
-of form fields.
-If you look at the
-{{< extlink "https://docs.djangoproject.com/en/4.1/ref/forms/fields/" "Form fields" >}} reference
-and compare the list
-of types
-to those in the
-{{< extlink "https://docs.djangoproject.com/en/4.1/ref/models/fields/" "Model field reference" >}},
-you can observe a lot of overlap.
-
-Like their form counterparts,
-models have `CharField`,
-`BooleanField`,
-`DateField`,
-`DateTimeField`,
-and many other similar types.
-The field types share many common attributes.
-Most commonly,
-I think you will use or encounter the following attributes.
-
-* `default` - If you want to be able to create a model record
- without specifying certain values,
- then you can use `default`.
- The value can either be a literal value
- or callable function that produces a value.
+that Django's form system includes a wide variety of form fields. If you look at the {{< extlink "https://docs.djangoproject.com/en/4.1/ref/forms/fields/" "Form fields" >}} reference and compare the list of types to those in the {{< extlink "https://docs.djangoproject.com/en/4.1/ref/models/fields/" "Model field reference" >}}, you can observe a lot of overlap.
+
+Like their form counterparts, models have `CharField`, `BooleanField`, `DateField`, `DateTimeField`, and many other similar types. The field types share many common attributes. Most commonly, I think you will use or encounter the following attributes:
+
+* `default` - If you want to be able to create a model record without specifying certain values, then you can use `default`. The value can either be a literal value or callable function that produces a value:
```python
# application/models.py
@@ -808,12 +408,7 @@ class DungeonsAndDragonsCharacter(
)
```
-* `unique` - When a field value must be unique
- for all the rows in the database table,
- use `unique`.
- This is a good attribute
- for identifiers
- where you don't expect duplicates.
+* `unique` - When a field value must be unique for all the rows in the database table, use `unique`. This is a good attribute for identifiers where you don't expect duplicates:
```python
class ImprobableHero(models.Model):
@@ -828,28 +423,7 @@ ImprobableHero.objects.create(
)
```
-* `null` - A relational database has the ability
- to store the absence of data.
- In the database,
- this value is thought of as `NULL`.
- Sometimes this is an important distinction
- versus a value that is empty.
- For instance,
- on a `Person` model,
- an integer field like `number_of_children` would mean very different things
- for a value of 0
- versus a value of `NULL`.
- The former indicates that a person has no children
- while the latter indicates
- that the number of children is unknown.
- The presence of null conditions requires more checking
- in your code
- so Django defaults to making `null` be `False`.
- This means that a field does not allow `NULL`.
- Null values can be useful if needed,
- but I think its better to avoid them
- if you can
- and try to keep actual data about a field.
+* `null` - A relational database has the ability to store the absence of data. In the database, this value is thought of as `NULL`. Sometimes this is an important distinction versus a value that is empty. For instance, on a `Person` model, an integer field like `number_of_children` would mean very different things for a value of 0 versus a value of `NULL`. The former indicates that a person has no children while the latter indicates that the number of children is unknown. The presence of null conditions requires more checking in your code so Django defaults to making `null` be `False`. This means that a field does not allow `NULL`. Null values can be useful if needed, but I think its better to avoid them if you can and try to keep actual data about a field:
```python
class Person(models.Model):
@@ -864,32 +438,10 @@ class Person(models.Model):
```
{{< web >}}
-* `blank` - The `blank` attribute is often used
- in conjunction with the `null` attribute.
- While the `null` attribute allows a database
- to store `NULL`
- for a field,
- `blank` allows *form validation*
- to permit an empty field.
- This is used by forms
- which are automatically generated
- by Django
- like in the Django administrator site
- which we'll talk about in the next article.
+* `blank` - The `blank` attribute is often used in conjunction with the `null` attribute. While the `null` attribute allows a database to store `NULL` for a field, `blank` allows *form validation* to permit an empty field. This is used by forms which are automatically generated by Django like in the Django administrator site which we'll talk about in the next article:
{{< /web >}}
{{< book >}}
-* `blank` - The `blank` attribute is often used
- in conjunction with the `null` attribute.
- While the `null` attribute allows a database
- to store `NULL`
- for a field,
- `blank` allows *form validation*
- to permit an empty field.
- This is used by forms
- which are automatically generated
- by Django
- like in the Django administrator site
- which we'll talk about in the next chapter.
+* `blank` - The `blank` attribute is often used in conjunction with the `null` attribute. While the `null` attribute allows a database to store `NULL` for a field, `blank` allows *form validation* to permit an empty field. This is used by forms which are automatically generated by Django like in the Django administrator site which we'll talk about in the next chapter:
{{< /book >}}
```python
@@ -904,30 +456,10 @@ class Pet(models.Model):
```
{{< web >}}
-* `choices` - We saw `choices`
- in the forms article
- as a technique
- for helping users pick the right value
- from a constrained set.
- `choices` can be set on the model.
- Django can do validation
- on the model
- that will ensure
- that only particular values are stored
- in a database field.
+* `choices` - We saw `choices` in the forms article as a technique for helping users pick the right value from a constrained set. `choices` can be set on the model. Django can do validation on the model that will ensure that only particular values are stored in a database field:
{{< /web >}}
{{< book >}}
-* `choices` - We saw `choices`
- in the forms chapter
- as a technique
- for helping users pick the right value
- from a constrained set.
- `choices` can be set on the model.
- Django can do validation
- on the model
- that will ensure
- that only particular values are stored
- in a database field.
+* `choices` - We saw `choices` in the forms chapter as a technique for helping users pick the right value from a constrained set. `choices` can be set on the model. Django can do validation on the model that will ensure that only particular values are stored in a database field:
{{< /book >}}
```python
@@ -945,17 +477,7 @@ class Car(models.Model):
)
```
-* `help_text` - As applications get bigger
- or if you work on a large team
- with many people creating Django models,
- the need for documentation grows.
- Django permits help text
- that can be displayed
- with a field value
- in the Django administrator site.
- This help text is useful
- to remind your future self
- or educate a coworker.
+* `help_text` - As applications get bigger or if you work on a large team with many people creating Django models, the need for documentation grows. Django permits help text that can be displayed with a field value in the Django administrator site. This help text is useful to remind your future self or educate a coworker:
```python
class Policy(models.Model):
@@ -970,33 +492,20 @@ class Policy(models.Model):
)
```
-Those are the attributes
-that I believe users are most likely
-to encounter.
-There are also a couple of important field types
-that require special attention:
-relational fields.
+Those are the attributes that I believe users are most likely to encounter. There are also a couple of important field types that require special attention: relational fields.
## What Makes A Database "Relational?"
-Relational databases have the ability
-to link different types
-of data together.
-We got a brief example
-of this earlier
+Relational databases have the ability to link different types of data together. We got a brief example of this earlier
{{< web >}}
in this article
{{< /web >}}
{{< book >}}
in this chapter
{{< /book >}}
-when we considered
-an employee
-with multiple phone numbers.
+when we considered an employee with multiple phone numbers.
-An overly simplified model
-for an employee
-with multiple phone numbers might look like:
+An overly simplified model for an employee with multiple phone numbers might look like:
```python
# application/models.py
@@ -1020,25 +529,12 @@ class Employee(models.Model):
)
```
-This single table could hold a couple
-of numbers,
-but this solution has some deficiencies.
-
-* What if an employee has more than two phone numbers?
- It's possible for a person to have multiple cell phones,
- a land line at their residence,
- a pager number,
- a fax number,
- and so on.
-* How can we know what type of phone number is
- in `phone_number_1` and `phone_number_2`?
- If you pull the employee record
- to try to call the individual
- and dial a fax number instead,
- you'd have a hard time talking to them.
-
-Instead,
-what if we had two separate models?
+This single table could hold a couple of numbers, but this solution has some deficiencies:
+
+* What if an employee has more than two phone numbers? It's possible for a person to have multiple cell phones, a land line at their residence, a pager number, a fax number, and so on.
+* How can we know what type of phone number is in `phone_number_1` and `phone_number_2`? If you pull the employee record to try to call the individual and dial a fax number instead, you'd have a hard time talking to them.
+
+Instead, what if we had two separate models?:
```python
# application/models.py
@@ -1071,12 +567,7 @@ class PhoneNumber(models.Model):
)
```
-We've got two separate tables.
-How can we link the tables
-so that an employee can have one, two, or two hundred phone numbers?
-For that,
-we can use the `ForeignKey` relational field type.
-Here is a slightly updated version of `PhoneNumber`.
+We've got two separate tables. How can we link the tables so that an employee can have one, two, or two hundred phone numbers? For that, we can use the `ForeignKey` relational field type. Here is a slightly updated version of `PhoneNumber`:
```python
...
@@ -1101,61 +592,22 @@ class PhoneNumber(models.Model):
)
```
-This update says that every phone number must now be associated
-with an employee record.
-`on_delete` is a required attribute
-that determines what will happen
-when an employee record gets deleted.
-In this case, `CASCADE` means
-that deleting an employee row will cascade
-and delete any of the employee's related phone numbers too.
-
-The key to how this works lies in understanding *keys*.
-When you make a Django model,
-you get an extra field added
-to your model
-by the framework.
-This field is called an `AutoField`.
+This update says that every phone number must now be associated with an employee record. `on_delete` is a required attribute that determines what will happen when an employee record gets deleted. In this case, `CASCADE` means that deleting an employee row will cascade and delete any of the employee's related phone numbers too.
+
+The key to how this works lies in understanding *keys*. When you make a Django model, you get an extra field added to your model by the framework. This field is called an `AutoField`:
```python
# This is what Django adds to your model.
id = models.AutoField(primary_key=True)
```
-An `AutoField` adds a column
-to a database table
-that will assign each row
-in the table
-a unique integer.
-Each new row increments
-from the previous row
-and numbering starts
-at one.
-This number is the identifier
-for the row
-and is called the *primary key*.
-
-If Tom Bombadil is the first employee
-in the table,
-then the row `id` value would be `1`.
-
-Knowing about primary keys,
-we're equipped to understand foreign key fields.
-In the case
-of the `PhoneNumber.employee` foreign key field,
-any phone number row will store the value
-of some employee row's primary key.
-This is usually called a one to many relationship.
-
-A `ForeignKey` is a one to many relationship
-because multiple rows from a table
-(in this case, `PhoneNumber`)
-can reference a single row
-in another table, namely, `Employee`.
-In other words,
-an employee can have multiple phone numbers.
-If we wanted to get Tom's phone numbers,
-then one possible way would be:
+An `AutoField` adds a column to a database table that will assign each row in the table a unique integer. Each new row increments from the previous row and numbering starts at one. This number is the identifier for the row and is called the *primary key*.
+
+If Tom Bombadil is the first employee in the table, then the row `id` value would be `1`.
+
+Knowing about primary keys, we're equipped to understand foreign key fields. In the case of the `PhoneNumber.employee` foreign key field, any phone number row will store the value of some employee row's primary key. This is usually called a one to many relationship.
+
+A `ForeignKey` is a one to many relationship because multiple rows from a table (in this case, `PhoneNumber`) can reference a single row in another table, namely, `Employee`. In other words, an employee can have multiple phone numbers. If we wanted to get Tom's phone numbers, then one possible way would be:
```python
tom = Employee.objects.get(
@@ -1177,38 +629,13 @@ FROM "application_phonenumber"
WHERE "application_phonenumber"."employee_id" = 1
```
-In the database,
-Django will store the table column
-for the foreign key
-as `employee_id`.
-The query is asking for all phone number rows
-that match when the employee ID is 1.
-Since primary keys have to be unique,
-that value of 1 can only match Tom Bombadil
-so the resulting rows will be phone numbers
-that are associated
-with that employee.
-
-There is another relational field type
-that we should spend time on.
-That field is the `ManyToManyField`.
-As you might guess,
-this field is used when two types
-of data relate to each other
-in a many to many fashion.
-
-Let's think about neighborhoods.
-Neighborhoods can have a variety
-of mixed residence types
-like houses, apartments, condominiums
-and so on,
-but to keep the example simpler,
-we'll assume that neighborhoods are composed
-of houses.
-Each house in a neighborhood is the home
-of one or more people.
-
-What if we tried to model this with `ForeignKey` fields?
+In the database, Django will store the table column for the foreign key as `employee_id`. The query is asking for all phone number rows that match when the employee ID is 1. Since primary keys have to be unique, that value of 1 can only match Tom Bombadil so the resulting rows will be phone numbers that are associated with that employee.
+
+There is another relational field type that we should spend time on. That field is the `ManyToManyField`. As you might guess, this field is used when two types of data relate to each other in a many to many fashion.
+
+Let's think about neighborhoods. Neighborhoods can have a variety of mixed residence types like houses, apartments, condominiums and so on, but to keep the example simpler, we'll assume that neighborhoods are composed of houses. Each house in a neighborhood is the home of one or more people.
+
+What if we tried to model this with `ForeignKey` fields?:
```python
# application/models.py
@@ -1229,14 +656,7 @@ class House(models.Model):
)
```
-This version shows a scenario
-where a house can only have a single resident.
-A person could be the resident
-of multiple houses,
-but those houses would be pretty lonely.
-What if we put the foreign key
-on the other side
-of the modeling relationship?
+This version shows a scenario where a house can only have a single resident. A person could be the resident of multiple houses, but those houses would be pretty lonely. What if we put the foreign key on the other side of the modeling relationship?:
```python
# application/models.py
@@ -1257,23 +677,11 @@ class Person(models.Model):
)
```
-In this version,
-a house can have multiple residents,
-but a person can only belong to a single house.
+In this version, a house can have multiple residents, but a person can only belong to a single house.
-Neither of these scenarios model the real world well.
-In the real world,
-houses can and do often hold multiple people.
-Simultaneously,
-many people in the world have a second house
-like a beach house
-or a summer cottage in the woods.
-Both sides of the model relationship can have many
-of the other side.
+Neither of these scenarios model the real world well. In the real world, houses can and do often hold multiple people. Simultaneously, many people in the world have a second house like a beach house or a summer cottage in the woods. Both sides of the model relationship can have many of the other side.
-With a `ManyToManyField`,
-you can add the field to either side.
-Here's the new modeling.
+With a `ManyToManyField`, you can add the field to either side. Here's the new modeling:
```python
# application/models.py
@@ -1293,38 +701,14 @@ class House(models.Model):
)
```
-How does this work at the database level?
-We saw with foreign keys
-that one table can hold the primary key
-of another table's row
-in its own data.
-Unfortunately,
-a single database column cannot hold multiple foreign keys.
-That means that the modeling above does *not* add `residents`
-to the `House` table.
-Instead,
-the relationship is handled
-by adding a *new* database table.
-This new table contains the mapping
-between people and houses
-and stores rows
-that contain primary keys from each model.
-
-Let's think of an example
-to see what this looks like.
-Suppose there are three `Person` records
-with primary keys of 1, 2, and 3.
-Let's also suppose that there are three `House` records
-with primary keys of 97, 98, and 99.
-To prove that the many-to-many relationship works
-in both directions,
-we'll assume these conditions are true:
+How does this work at the database level? We saw with foreign keys that one table can hold the primary key of another table's row in its own data. Unfortunately, a single database column cannot hold multiple foreign keys. That means that the modeling above does *not* add `residents` to the `House` table. Instead, the relationship is handled by adding a *new* database table. This new table contains the mapping between people and houses and stores rows that contain primary keys from each model.
+
+Let's think of an example to see what this looks like. Suppose there are three `Person` records with primary keys of 1, 2, and 3. Let's also suppose that there are three `House` records with primary keys of 97, 98, and 99. To prove that the many-to-many relationship works in both directions, we'll assume these conditions are true:
* People with primary keys of 1 and 2 reside in house 97.
* The person with primary key 3 owns house 98 and 99.
-The data in the new mapping table between `Person` and `House`
-would contain data like:
+The data in the new mapping table between `Person` and `House` would contain data like:
```text
Person | House
@@ -1335,26 +719,11 @@ Person | House
3 | 99
```
-Because of the joining table,
-Django is able to query either side of the table
-to get related houses or residents.
-
-We can access the "many" side
-of each model using a queryset.
-`residents` will be a `ManyRelatedManager`
-and, like other managers,
-can provide querysets
-by using certain manager methods.
-
-Getting the reverse direction is a little less obvious.
-Django will add another `ManyRelatedManager`
-to the `Person` model automatically.
-The name of that manager is the model name joined with `_set`.
-In this case,
-that name is `house_set`.
-You can also provide a `related_name` attribute
-to the `ManyToManyField` if you want a different name
-like if you wanted to call it `houses` instead.
+Because of the joining table, Django is able to query either side of the table to get related houses or residents.
+
+We can access the "many" side of each model using a queryset. `residents` will be a `ManyRelatedManager` and, like other managers, can provide querysets by using certain manager methods.
+
+Getting the reverse direction is a little less obvious. Django will add another `ManyRelatedManager` to the `Person` model automatically. The name of that manager is the model name joined with `_set`. In this case, that name is `house_set`. You can also provide a `related_name` attribute to the `ManyToManyField` if you want a different name like if you wanted to call it `houses` instead:
```python
house = House.objects.get(
@@ -1369,13 +738,7 @@ for house in person.house_set.all():
print(house.address)
```
-Understanding the Django models fields
-of `ForeignKey` and `ManyToManyField` is an important step
-to modeling your problem domain well.
-By having these tools available to you,
-you can begin to create many
-of the complex data relationships that exist
-with real world problems.
+Understanding the Django models fields of `ForeignKey` and `ManyToManyField` is an important step to modeling your problem domain well. By having these tools available to you, you can begin to create many of the complex data relationships that exist with real world problems.
## Summary
@@ -1389,17 +752,12 @@ we've explored:
* How to set up a database for your project.
* How Django uses special classes called models to keep data.
-* Running the commands that will prepare a database
- for the models you want to use.
+* Running the commands that will prepare a database for the models you want to use.
* Saving new information into the database.
* Asking the database for information that we stored.
* Complex field types to model real world problems.
-With this ability to store data
-into a database,
-**you have all the core tools
-for building an interactive website
-for your users!**
+With this ability to store data into a database, **you have all the core tools for building an interactive website for your users!**
{{< web >}}
In this series,
{{< /web >}}
@@ -1414,36 +772,15 @@ we have examined:
* forms to let users input and interact with your site
* models to store data into a database for long term storage
-This is the core set of features
-that most websites have.
-Since we've seen the core topics
-that make Django sites work,
-we're ready to focus our attention
-on some of the other amazing tools
-that set Django apart
-from the pack.
-
-First on the list is the built-in
-Django administrators site
-that lets you explore the data
-that you store
-in your database.
-We'll cover:
+This is the core set of features that most websites have. Since we've seen the core topics that make Django sites work, we're ready to focus our attention on some of the other amazing tools that set Django apart from the pack.
+
+First on the list is the built-in Django administrators site that lets you explore the data that you store in your database. We'll cover:
* What the Django admin site is
* How to make your models appear in the admin
* How to create extra actions that your admin users can do
{{< web >}}
-If you'd like to follow along
-with the series,
-please feel free to sign up
-for my newsletter
-where I announce all of my new content.
-If you have other questions,
-you can reach me online
-on Twitter
-where I am
-{{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
+If you'd like to follow along with the series, please feel free to sign up for my newsletter where I announce all of my new content. If you have other questions, you can reach me online on Twitter where I am {{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
{{< /web >}}
From 37c42913677e61272165b3a6dec2b73918ff9af4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nazar=C3=A9=20da=20Piedade?=
Date: Fri, 5 May 2023 11:13:31 +0100
Subject: [PATCH 08/30] chore: format
`understand-django/2020-08-26-administer-all-the-things.pt.md`
---
.../2020-03-03-views-on-views.pt.md | 4 +-
...2020-04-02-templates-user-interfaces.pt.md | 4 +-
.../2020-05-05-user-interaction-forms.pt.md | 2 +-
.../2020-06-25-store-data-with-models.pt.md | 2 +-
...2020-08-26-administer-all-the-things.pt.md | 739 +++---------------
5 files changed, 127 insertions(+), 624 deletions(-)
diff --git a/content/understand-django/2020-03-03-views-on-views.pt.md b/content/understand-django/2020-03-03-views-on-views.pt.md
index 9adc956b..a951e0de 100644
--- a/content/understand-django/2020-03-03-views-on-views.pt.md
+++ b/content/understand-django/2020-03-03-views-on-views.pt.md
@@ -15,14 +15,14 @@ tags:
---
{{< web >}}
-In the previous [Understand Django]({{< ref "/understand-django/_index.md" >}}) article, I covered URLs and the variety of tools that Django gives us to describe the outside interface to the internet for your project. In this article,
+In the previous [Understand Django]({{< ref "/understand-django/_index.pt.md" >}}) article, I covered URLs and the variety of tools that Django gives us to describe the outside interface to the internet for your project. In this article,
{{< /web >}}
{{< book >}}
Now that we have a grasp on URLs in Django,
{{< /book >}}
we'll examine the core building block that makes those URLs work: the Django view.
-{{< understand-django-series "views" >}}
+{{< understand-django-series-pt "views" >}}
## What Is A View?
diff --git a/content/understand-django/2020-04-02-templates-user-interfaces.pt.md b/content/understand-django/2020-04-02-templates-user-interfaces.pt.md
index 40287b34..19fff2f8 100644
--- a/content/understand-django/2020-04-02-templates-user-interfaces.pt.md
+++ b/content/understand-django/2020-04-02-templates-user-interfaces.pt.md
@@ -15,11 +15,11 @@ tags:
---
{{< web >}}
-In the previous [Understand Django]({{< ref "/understand-django/_index.md" >}}) article, we looked at the fundamentals of using views in Django. This article will focus on templates.
+In the previous [Understand Django]({{< ref "/understand-django/_index.pt.md" >}}) article, we looked at the fundamentals of using views in Django. This article will focus on templates.
{{< /web >}}
Templates are your primary tool in a Django project for generating a user interface. With templates, you'll be able to build the pages that users will see when they visit your web app. Let's see how templates hook into views and what features Django provides with its template system.
-{{< understand-django-series "templates" >}}
+{{< understand-django-series-pt "templates" >}}
## Set Up Templates
diff --git a/content/understand-django/2020-05-05-user-interaction-forms.pt.md b/content/understand-django/2020-05-05-user-interaction-forms.pt.md
index a8a21e24..92d99dda 100644
--- a/content/understand-django/2020-05-05-user-interaction-forms.pt.md
+++ b/content/understand-django/2020-05-05-user-interaction-forms.pt.md
@@ -15,7 +15,7 @@ tags:
---
{{< web >}}
-In the previous [Understand Django]({{< ref "/understand-django/_index.md" >}}) article, we saw how Django templates work to produce a user interface. That's fine if you only need to display a user interface, but what do you do
+In the previous [Understand Django]({{< ref "/understand-django/_index.pt.md" >}}) article, we saw how Django templates work to produce a user interface. That's fine if you only need to display a user interface, but what do you do
{{< /web >}}
{{< book >}}
What do you do
diff --git a/content/understand-django/2020-06-25-store-data-with-models.pt.md b/content/understand-django/2020-06-25-store-data-with-models.pt.md
index 8a312063..d9965550 100644
--- a/content/understand-django/2020-06-25-store-data-with-models.pt.md
+++ b/content/understand-django/2020-06-25-store-data-with-models.pt.md
@@ -15,7 +15,7 @@ tags:
---
{{< web >}}
-In the previous [Understand Django]({{< ref "/understand-django/_index.md" >}}) article, we encountered forms and how forms allow your application to receive data from users who use your site. In this article, you'll see how to take that data
+In the previous [Understand Django]({{< ref "/understand-django/_index.pt.md" >}}) article, we encountered forms and how forms allow your application to receive data from users who use your site. In this article, you'll see how to take that data
{{< /web >}}
{{< book >}}
In this chapter, you'll see how to take data
diff --git a/content/understand-django/2020-08-26-administer-all-the-things.pt.md b/content/understand-django/2020-08-26-administer-all-the-things.pt.md
index de0b6ce8..7974c7ba 100644
--- a/content/understand-django/2020-08-26-administer-all-the-things.pt.md
+++ b/content/understand-django/2020-08-26-administer-all-the-things.pt.md
@@ -1,14 +1,7 @@
---
title: "Administer All The Things"
description: >-
- This article will look
- at how maintainers
- of an application
- can manage their data
- through Django's built-in administrative tools.
- We will see how to build admin pages
- and customize the admin tools
- to help teams navigate their apps.
+ This article will look at how maintainers of an application can manage their data through Django's built-in administrative tools. We will see how to build admin pages and customize the admin tools to help teams navigate their apps.
image: img/django.png
type: post
categories:
@@ -23,175 +16,63 @@ series: "Understand Django"
---
{{< web >}}
-In the previous
-[Understand Django]({{< ref "/understand-django/_index.md" >}})
-article,
-we used models
-to see how Django stores data
-in a relational database.
+In the previous [Understand Django]({{< ref "/understand-django/_index.pt.md" >}}) article, we used models to see how Django stores data in a relational database.
{{< /web >}}
-We covered all the tools
-to bring your data to life
-in your application.
+We covered all the tools to bring your data to life in your application.
{{< web >}}
In this article,
{{< /web >}}
{{< book >}}
In this chapter,
{{< /book >}}
-we will focus
-on the built-in tools
-that Django provides
-to help us manage that data.
+we will focus on the built-in tools that Django provides to help us manage that data.
-{{< understand-django-series "admin" >}}
+{{< understand-django-series-pt "admin" >}}
## What Is The Django Admin?
-When you run an application,
-you'll find data
-that needs special attention.
-Maybe you're creating a blog
-and need to create and edit tags or categories.
-Perhaps you have an online shop
-and need to manage your inventory.
-Whatever you're building,
-you'll probably have to manage *something*.
-
-How can you manage that data?
-
-* If you're a programmer,
- you can probably log into your server,
- fire up a Django management shell,
- and work with data directly
- using Python.
-* If you're not a programmer,
- well,
- I guess you're out of luck!
- **Nope, that's not true!**
-
-Django includes a web administrative interface
-that can help programmers and non-programmers alike.
-This administrative interface is usually called the Django admin,
-for short.
-
-Like so many other extensions
-in the Django ecosystem,
-the admin site is a Django application.
-The site is so commonly used
-that it is pre-configured
-when you run the `startproject` command.
-
-Before proceeding,
-I'd first like to make note of a security issue.
-When using `startproject`,
-Django will put the admin site
-at `/admin/`
-by default.
-**Change this**.
-The starter template conveniently sets up the admin site
-for you,
-but this default URL makes it easy
-for {{< extlink "https://en.wikipedia.org/wiki/Script_kiddie" "script kiddies" >}}
-to try to attack your admin site
-to gain access.
-Putting your admin site on a different URL *won't* fully protect your site
-(because you should never rely
-on "security through obscurity"),
-but it will help avoid a large amount
-of automated attacks.
-
-The Django admin gives you a quick ability
-to interact
-with your models.
-As you will see shortly,
-you can register a model
-with the admin site.
-Once the model is registered,
-you can use the site interface
-to perform CRUD operations
-on the data.
-
-CRUD is an acronym
-that describes the primary functions
-of many websites.
-The acronym stands for:
+When you run an application, you'll find data that needs special attention. Maybe you're creating a blog and need to create and edit tags or categories. Perhaps you have an online shop and need to manage your inventory. Whatever you're building, you'll probably have to manage *something*.
+
+How can you manage that data?:
+
+* If you're a programmer, you can probably log into your server, fire up a Django management shell, and work with data directly using Python.
+* If you're not a programmer, well, I guess you're out of luck! **Nope, that's not true!**
+
+Django includes a web administrative interface that can help programmers and non-programmers alike. This administrative interface is usually called the Django admin, for short.
+
+Like so many other extensions in the Django ecosystem, the admin site is a Django application. The site is so commonly used that it is pre-configured when you run the `startproject` command.
+
+Before proceeding, I'd first like to make note of a security issue. When using `startproject`, Django will put the admin site at `/admin/` by default. **Change this**. The starter template conveniently sets up the admin site for you, but this default URL makes it easy for {{< extlink "https://en.wikipedia.org/wiki/Script_kiddie" "script kiddies" >}} to try to attack your admin site to gain access. Putting your admin site on a different URL *won't* fully protect your site (because you should never rely on "security through obscurity"), but it will help avoid a large amount of automated attacks.
+
+The Django admin gives you a quick ability to interact with your models. As you will see shortly, you can register a model with the admin site. Once the model is registered, you can use the site interface to perform CRUD operations on the data.
+
+CRUD is an acronym that describes the primary functions of many websites. The acronym stands for:
* **Create** - A website can create data (i.e., insert data into the database)
* **Read** - Users can see the data
* **Update** - Data can be updated by users
* **Delete** - A user can delete data from the system
-If you think about the actions
-that you take on a website,
-most actions will fall
-into one of those four categories.
-
-The admin site provides tools
-for doing all of those operations.
-There are a few main pages
-that you can navigate
-when working in a Django admin site
-that direct where the CRUD operations happen.
-These pages are available to you
-with very little effort
-on your part
-aside from the registration process
-that you'll see
-in the next section.
-
-1. Admin index page -
- This page will show all the models,
- grouped by the Django application they originate from,
- that are registered with the admin.
-2. Model list page -
- The list page shows rows of data
- from a model (i.e., a database table).
- From this page,
- an administrator can perform actions
- on multiple database records
- like deleting a set of records
- in a single operation.
-3. Add model page -
- The admin provides a page
- where new model instances can be created
- using automatically generated forms
- based on the model's fields.
-4. Model change page -
- The change page lets you update an existing model instance
- (i.e., a database table row).
- From this page,
- you can also delete a model instance.
-
-If you inspect this small set of pages,
-you'll notice
-that every part of the CRUD acronym can occur
-in this admin site.
-The power to create and destroy is in your hands. 😈
-
-Now that we understand what is in the admin site,
-let's focus on how to add your models to the admin.
+If you think about the actions that you take on a website, most actions will fall into one of those four categories.
+
+The admin site provides tools for doing all of those operations. There are a few main pages that you can navigate when working in a Django admin site that direct where the CRUD operations happen. These pages are available to you with very little effort on your part aside from the registration process that you'll see in the next section:
+
+1. Admin index page - This page will show all the models, grouped by the Django application they originate from, that are registered with the admin.
+2. Model list page - The list page shows rows of data from a model (i.e., a database table). From this page, an administrator can perform actions on multiple database records like deleting a set of records in a single operation.
+3. Add model page - The admin provides a page where new model instances can be created using automatically generated forms based on the model's fields.
+4. Model change page - The change page lets you update an existing model instance (i.e., a database table row). From this page, you can also delete a model instance.
+
+If you inspect this small set of pages, you'll notice that every part of the CRUD acronym can occur in this admin site. The power to create and destroy is in your hands. 😈
+
+Now that we understand what is in the admin site, let's focus on how to add your models to the admin.
## Register A Model With The Admin
-To make the admin site show your model data,
-we need to update `admin.py`.
-On a new application created
-with `startapp`,
-you'll find
-that the `admin.py` file is largely empty.
-We need to provide a bit
-of glue
-so that the admin knows
-about a model.
-
-The admin site expects a `ModelAdmin` class
-for every model
-that you want to see displayed
-within the site.
-
-Let's consider a crude modeling
-of a book.
+To make the admin site show your model data, we need to update `admin.py`. On a new application created with `startapp`, you'll find that the `admin.py` file is largely empty. We need to provide a bit of glue so that the admin knows about a model.
+
+The admin site expects a `ModelAdmin` class for every model that you want to see displayed within the site.
+
+Let's consider a crude modeling of a book:
```python
# application/models.py
@@ -206,8 +87,7 @@ class Book(models.Model):
)
```
-Now we can create a `ModelAdmin` class
-for the `Book` model.
+Now we can create a `ModelAdmin` class for the `Book` model:
```python
# application/admin.py
@@ -220,20 +100,12 @@ class BookAdmin(admin.ModelAdmin):
pass
```
-There are a couple of important items
-to observe
-with this `admin.py` file.
+There are a couple of important items to observe with this `admin.py` file:
-1. The `BookAdmin` is a subclass
- of `admin.ModelAdmin`.
-2. The `BookAdmin` is registered
- with the admin site
- by using the `admin.register` decorator.
+1. The `BookAdmin` is a subclass of `admin.ModelAdmin`.
+2. The `BookAdmin` is registered with the admin site by using the `admin.register` decorator.
-You can also register an admin class
-by calling `register`
-after the class
-if you don't want to use a decorator.
+You can also register an admin class by calling `register` after the class if you don't want to use a decorator:
```python
# application/admin.py
@@ -247,34 +119,11 @@ class BookAdmin(admin.ModelAdmin):
admin.site.register(Book, BookAdmin)
```
-Now that we have a model registered
-with the admin site,
-how do we view it?
-Fire up your trusty development server
-with `runserver`
-and visit the URL
-that used to be `/admin/`
-(because you did change
-to something different from `/admin/`, right? Right!?).
-
-On this page,
-you'll encounter a login screen.
-We haven't worked through the authentication system yet,
-but, for now,
-we can understand that only user accounts
-that have a staff level permission
-can log in.
-
-Django provides a command
-that will let us create a user account
-with staff level permission
-and all other permissions.
-Like Linux operating systems,
-the user account
-with all permissions
-is called a superuser.
-You can create a superuser account
-with the `createsuperuser` command.
+Now that we have a model registered with the admin site, how do we view it? Fire up your trusty development server with `runserver` and visit the URL that used to be `/admin/` (because you did change to something different from `/admin/`, right? Right!?).
+
+On this page, you'll encounter a login screen. We haven't worked through the authentication system yet, but, for now, we can understand that only user accounts that have a staff level permission can log in.
+
+Django provides a command that will let us create a user account with staff level permission and all other permissions. Like Linux operating systems, the user account with all permissions is called a superuser. You can create a superuser account with the `createsuperuser` command:
```bash
$ ./manage.py createsuperuser
@@ -285,88 +134,21 @@ Password (again):
Superuser created successfully.
```
-With a superuser account available,
-you're ready to log in
-to the admin site.
-Because you'll be using a superuser account,
-you will have permission
-to see every model
-that is registered
-with the admin site.
-
-Once you've logged in,
-you can view the `Book` model's admin page.
-Poke around!
-Create a book with the "Add Book" button.
-View the list page.
-Edit the book.
-Delete the book.
-You can see that with a tiny amount
-of work
-on your part,
-Django gives you a full CRUD interface
-for interacting with your model.
-
-We added the most simple `ModelAdmin` possible.
-The body of the class was a `pass`
-instead of any attributes.
-Django gives us a ton
-of options
-to let us control how our admin pages
-for `Book` will behave.
-Let's go on a tour
-of some commonly used admin attributes.
+With a superuser account available, you're ready to log in to the admin site. Because you'll be using a superuser account, you will have permission to see every model that is registered with the admin site.
+
+Once you've logged in, you can view the `Book` model's admin page. Poke around! Create a book with the "Add Book" button. View the list page. Edit the book. Delete the book. You can see that with a tiny amount of work on your part, Django gives you a full CRUD interface for interacting with your model.
+
+We added the most simple `ModelAdmin` possible. The body of the class was a `pass` instead of any attributes. Django gives us a ton of options to let us control how our admin pages for `Book` will behave. Let's go on a tour of some commonly used admin attributes.
## Customizing Your Admin
-Like many other parts of Django,
-the framework uses class level attributes
-to define the behavior
-of a class.
-Unlike forms and models
-where class level attributes are mostly fields
-that you're defining
-for yourself,
-`ModelAdmin` classes provide values
-for attributes
-that are well defined
-in the documentation.
-These attributes act as hooks
-that let you customize the behavior
-of your admin pages.
-
-Making effective admin pages is primarily about
-using these attributes
-so that the `ModelAdmin` class will do what you want.
-As such,
-mastering the Django admin site
-is all about mastering the `ModelAdmin` options
-that are listed
-{{< extlink "https://docs.djangoproject.com/en/4.1/ref/contrib/admin/#modeladmin-options" "in the documentation" >}}.
-That list is long,
-but don't be discouraged!
-I think that you can get about 80%
-of the value out of the Django admin
-by knowing only a handful
-of the options.
-
-When you poked around
-on the `Book` pages,
-you probably noticed
-that the listing of books is quite bland.
-The default list looks something
-like a list of links
-that show `Book object (#)`.
-We can change the look and utility
-of this page
-with a few different settings.
-
-Let's start with `list_display`.
-This `ModelAdmin` attribute controls
-which fields will appear
-on the list page.
-With our book model example,
-we could add the title to the page.
+Like many other parts of Django, the framework uses class level attributes to define the behavior of a class. Unlike forms and models where class level attributes are mostly fields that you're defining for yourself, `ModelAdmin` classes provide values for attributes that are well defined in the documentation. These attributes act as hooks that let you customize the behavior of your admin pages.
+
+Making effective admin pages is primarily about using these attributes so that the `ModelAdmin` class will do what you want. As such, mastering the Django admin site is all about mastering the `ModelAdmin` options that are listed {{< extlink "https://docs.djangoproject.com/en/4.1/ref/contrib/admin/#modeladmin-options" "in the documentation" >}}. That list is long, but don't be discouraged! I think that you can get about 80% of the value out of the Django admin by knowing only a handful of the options.
+
+When you poked around on the `Book` pages, you probably noticed that the listing of books is quite bland. The default list looks something like a list of links that show `Book object (#)`. We can change the look and utility of this page with a few different settings.
+
+Let's start with `list_display`. This `ModelAdmin` attribute controls which fields will appear on the list page. With our book model example, we could add the title to the page:
```python
# application/admin.py
@@ -376,24 +158,9 @@ class BookAdmin(admin.ModelAdmin):
list_display = ('id', 'title')
```
-Django will make whatever is listed first
-into the link
-that a user can click
-to view the admin detail page
-for a model record.
-In this example,
-I'm using the `id` field
-as the link,
-but I could have used a single element tuple
-of `('title',)` to make the page show
-only the titles
-with the titles
-being the links.
-
-Sometimes you will have a type of model
-where you only want to see a subset
-of the records.
-Suppose that the `Book` model has a category field.
+Django will make whatever is listed first into the link that a user can click to view the admin detail page for a model record. In this example, I'm using the `id` field as the link, but I could have used a single element tuple of `('title',)` to make the page show only the titles with the titles being the links.
+
+Sometimes you will have a type of model where you only want to see a subset of the records. Suppose that the `Book` model has a category field:
```python
# application/models.py
@@ -415,10 +182,7 @@ class Book(models.Model):
)
```
-By using the `list_filter` attribute,
-we can give the admin list page the ability
-to filter to the category
-that we want.
+By using the `list_filter` attribute, we can give the admin list page the ability to filter to the category that we want:
```python
# application/admin.py
@@ -429,25 +193,9 @@ class BookAdmin(admin.ModelAdmin):
list_filter = ('category',)
```
-The option will put a sidebar
-on the right side
-of the admin page.
-In that sidebar,
-you would see the categories
-that I included
-in the `Category` choices class.
-If I click on the "Fantasy" link,
-then my browser will navigate
-to `/admin/application/book/?category__exact=2`
-and will only display database rows
-that have a matching category.
-
-This isn't the only kind
-of filtering that the admin can do.
-We can also filter in time
-with the `date_hierarchy` field.
-Next,
-let's give the model a `published_date`.
+The option will put a sidebar on the right side of the admin page. In that sidebar, you would see the categories that I included in the `Category` choices class. If I click on the "Fantasy" link, then my browser will navigate to `/admin/application/book/?category__exact=2` and will only display database rows that have a matching category.
+
+This isn't the only kind of filtering that the admin can do. We can also filter in time with the `date_hierarchy` field. Next, let's give the model a `published_date`:
```python
# application/models.py
@@ -460,8 +208,7 @@ class Book(models.Model):
)
```
-We can also change the `ModelAdmin`
-to use the new field.
+We can also change the `ModelAdmin` to use the new field:
```python
# application/admin.py
@@ -473,32 +220,11 @@ class BookAdmin(admin.ModelAdmin):
list_filter = ("category",)
```
-By including the `date_hierarchy` attribute,
-the list page will contain some new user interface elements.
-Across the top of the page will be selectors
-to help filter down to the right time range.
-This is a very useful way to look through your database table.
-
-We can still go further.
-Perhaps we want all of the books to be sorted
-by their titles.
-Even if the `ordering` attribute is not set
-on the model's meta options,
-the `ModelAdmin` has its own `ordering` attribute.
-
-*What's "meta?"*
-Aside from fields,
-a Django model can set extra infromation
-about how to handle data.
-These extra options are the "meta" attributes
-of the model.
-A Django model adds meta info
-by including a nested `Meta` class
-on the model.
-Check out the
-{{< extlink "https://docs.djangoproject.com/en/4.1/ref/models/options/" "Model Meta options" >}}
-to see what other features are available
-to customize model behavior.
+By including the `date_hierarchy` attribute, the list page will contain some new user interface elements. Across the top of the page will be selectors to help filter down to the right time range. This is a very useful way to look through your database table.
+
+We can still go further. Perhaps we want all of the books to be sorted by their titles. Even if the `ordering` attribute is not set on the model's meta options, the `ModelAdmin` has its own `ordering` attribute.
+
+*What's "meta?"* Aside from fields, a Django model can set extra information about how to handle data. These extra options are the "meta" attributes of the model. A Django model adds meta info by including a nested `Meta` class on the model. Check out the {{< extlink "https://docs.djangoproject.com/en/4.1/ref/models/options/" "Model Meta options" >}} to see what other features are available to customize model behavior:
```python
# application/admin.py
@@ -511,17 +237,9 @@ class BookAdmin(admin.ModelAdmin):
ordering = ("title",)
```
-With this setting,
-all of the books
-on the page
-will be ordered by the title.
-The `ordering` attribute will add an appropriate
-`ORDER BY` clause to the database query
-via the admin-generated ORM `QuerySet`.
+With this setting, all of the books on the page will be ordered by the title. The `ordering` attribute will add an appropriate `ORDER BY` clause to the database query via the admin-generated ORM `QuerySet`.
-The final convenient list page option
-that I want to highlight
-is the `search_fields` option.
+The final convenient list page option that I want to highlight is the `search_fields` option:
```python
# application/admin.py
@@ -535,20 +253,9 @@ class BookAdmin(admin.ModelAdmin):
search_fields = ("author",)
```
-With this option,
-this list page will add a search bar
-to the top of the page.
-In the example,
-I added the ability to search based
-on the author
-of the book.
-
-When you search,
-your resulting URL could look
-like `/admin/application/book/?q=tolkien`.
-Django will do a case insensitive search
-on the field.
-The `QuerySet` would be something like:
+With this option, this list page will add a search bar to the top of the page. In the example, I added the ability to search based on the author of the book.
+
+When you search, your resulting URL could look like `/admin/application/book/?q=tolkien`. Django will do a case insensitive search on the field. The `QuerySet` would be something like:
```python
search_results = Book.objects.filter(
@@ -556,21 +263,11 @@ search_results = Book.objects.filter(
)
```
-The results wouldn't compete well
-compared to a dedicated search engine,
-but getting a decent search feature
-for a single line of code is awesome!
+The results wouldn't compete well compared to a dedicated search engine, but getting a decent search feature for a single line of code is awesome!
-The `ModelAdmin` also includes some useful settings
-to modify the behavior
-of the detail page
-of particular database records.
+The `ModelAdmin` also includes some useful settings to modify the behavior of the detail page of particular database records.
-For instance,
-let's assume
-that the `Book` model
-has a `ForeignKey`
-to track an editor.
+For instance, let's assume that the `Book` model has a `ForeignKey` to track an editor:
```python
# application/models.py
@@ -588,22 +285,9 @@ class Book(models.Model):
)
```
-On the admin page
-for an individual book,
-the `editor` field will be a dropdown
-by default.
-This field will include every `User` record
-in your application.
-If you have a popular site
-with thousands or millions of users,
-the page would be crushed
-under the weight
-of loading all those user records
-into that dropdown.
-
-Instead of having a useless page
-that you can't load,
-you can use `raw_id_fields`.
+On the admin page for an individual book, the `editor` field will be a dropdown by default. This field will include every `User` record in your application. If you have a popular site with thousands or millions of users, the page would be crushed under the weight of loading all those user records into that dropdown.
+
+Instead of having a useless page that you can't load, you can use `raw_id_fields`:
```python
# application/admin.py
@@ -618,40 +302,12 @@ class BookAdmin(admin.ModelAdmin):
search_fields = ("author",)
```
-By using `raw_id_fields`,
-the admin changes from using a dropdown
-to using a basic text input
-which will display the foreign key
-of the user record.
-Seeing a foreign key number is visually less useful
-than seeing the actual name selected
-in a dropdown,
-but the `raw_id_fields` option adds two features
-to alleviate this.
-
-1. A search icon is present.
- If users click on the icon,
- a popup window appears
- to let the user search
- for a record
- in a dedicated selection interface.
-2. If the record already has a foreign key
- for the field,
- then the string representation
- of the record
- will display next to the icon.
-
-Another option that can be useful
-is the `prepopulated_fields` option.
-Back in our discussion
-of URLs,
-we talked about slug fields.
-Slugs are often used
-to make pleasant URLs
-for detail pages
-showing an individual model instance.
-Let's add a `SlugField`
-to the `Book` model.
+By using `raw_id_fields`, the admin changes from using a dropdown to using a basic text input which will display the foreign key of the user record. Seeing a foreign key number is visually less useful than seeing the actual name selected in a dropdown, but the `raw_id_fields` option adds two features to alleviate this:
+
+1. A search icon is present. If users click on the icon, a popup window appears to let the user search for a record in a dedicated selection interface.
+2. If the record already has a foreign key for the field, then the string representation of the record will display next to the icon.
+
+Another option that can be useful is the `prepopulated_fields` option. Back in our discussion of URLs, we talked about slug fields. Slugs are often used to make pleasant URLs for detail pages showing an individual model instance. Let's add a `SlugField` to the `Book` model:
```python
# application/models.py
@@ -663,14 +319,7 @@ class Book(models.Model):
slug = models.SlugField()
```
-What is the benefit
-of `prepopulated_fields`?
-By using this option,
-we can instruct the admin site
-to populate the `slug` field
-based on the `title`
-of the book.
-Here's the update to the `ModelAdmin`.
+What is the benefit of `prepopulated_fields`? By using this option, we can instruct the admin site to populate the `slug` field based on the `title` of the book. Here's the update to the `ModelAdmin`:
```python
# application/admin.py
@@ -686,36 +335,11 @@ class BookAdmin(admin.ModelAdmin):
search_fields = ("author",)
```
-Now when we want to add a new book
-in the admin,
-Django will use some JavaScript
-to update the slug field dynamically
-as we type the title!
-
-To this point,
-every attribute that we've added
-to the admin
-is static configuration.
-What do you do if you want to vary
-how the admin pages behave
-based on something dynamic?
-
-Thankfully,
-the Django team thought of that too.
-All of the options
-that we've examined
-have an equivalent method you can override
-that is prefixed
-with `get_`.
-For instance,
-if we want to control
-what fields users see
-on the list page
-based on who they are,
-we would implement `get_list_display`.
-In that method,
-we would return a tuple
-based on the user's access level.
+Now when we want to add a new book in the admin, Django will use some JavaScript to update the slug field dynamically as we type the title!
+
+To this point, every attribute that we've added to the admin is static configuration. What do you do if you want to vary how the admin pages behave based on something dynamic?
+
+Thankfully, the Django team thought of that too. All of the options that we've examined have an equivalent method you can override that is prefixed with `get_`. For instance, if we want to control what fields users see on the list page based on who they are, we would implement `get_list_display`. In that method, we would return a tuple based on the user's access level:
```python
# application/admin.py
@@ -739,16 +363,9 @@ class BookAdmin(admin.ModelAdmin):
return ('id', 'title')
```
-One final attribute to consider
-is called `inlines`.
-I don't reach for this option often,
-but it's a convenient way
-to see *other* models
-that are related to a particular model.
+One final attribute to consider is called `inlines`. I don't reach for this option often, but it's a convenient way to see *other* models that are related to a particular model.
-Suppose our sample application has reviews
-for books.
-We could add a model like:
+Suppose our sample application has reviews for books. We could add a model like:
```python
# application/models.py
@@ -762,12 +379,7 @@ class Review(models.Model):
comment = models.TextField()
```
-To show other models
-on a detail page,
-we need to create an inline class
-and include it
-with the `ModelAdmin`.
-The result looks like:
+To show other models on a detail page, we need to create an inline class and include it with the `ModelAdmin`. The result looks like:
```python
# application/admin.py
@@ -790,62 +402,17 @@ class BookAdmin(admin.ModelAdmin):
search_fields = ("author",)
```
-By adding the inline class
-to the list of `inlines`,
-the detail page will show any reviews
-that are associated
-with a book.
-Additionally,
-you could create new reviews
-from the detail page
-since the admin will include a few blank forms
-by default.
-
-We've covered many options
-of the `ModelAdmin` class
-that you can use
-to customize your admin experience
-with common functions
-that many admin tools require.
-**What about the *uncommon* functions?**
-For extra customization,
-we can use admin actions.
+By adding the inline class to the list of `inlines`, the detail page will show any reviews that are associated with a book. Additionally, you could create new reviews from the detail page since the admin will include a few blank forms by default.
+
+We've covered many options of the `ModelAdmin` class that you can use to customize your admin experience with common functions that many admin tools require. **What about the *uncommon* functions?** For extra customization, we can use admin actions.
## Taking Action In The Admin
-When you want to do work
-related to specific records
-in your database,
-Django provides some techniques
-to customize your site
-and provide those capabilities.
-These customizations are called *actions*
-and they appear
-on the list page
-above the list
-of records.
-
-In the default admin site,
-there is an action
-that lets administrators delete records.
-If you select some rows
-with the checkboxes
-on the left hand side,
-select "Delete selected \",
-then click "Go",
-you will be presented
-with a page
-that asks for confirmation
-about deleting the rows you picked.
-
-The same kind of flow could be applied
-for any actions
-that you want
-to perform
-on database records.
-We can do this
-by adding a method
-on our `ModelAdmin`.
+When you want to do work related to specific records in your database, Django provides some techniques to customize your site and provide those capabilities. These customizations are called *actions* and they appear on the list page above the list of records.
+
+In the default admin site, there is an action that lets administrators delete records. If you select some rows with the checkboxes on the left hand side, select "Delete selected \", then click "Go", you will be presented with a page that asks for confirmation about deleting the rows you picked.
+
+The same kind of flow could be applied for any actions that you want to perform on database records. We can do this by adding a method on our `ModelAdmin`.
The method must follow this interface:
@@ -863,36 +430,11 @@ class MyModelAdmin(admin.ModelAdmin):
...
```
-The queryset will represent the set
-of model records
-that the user selected.
-If the method returns `None`,
-then the user will be returned
-to the same admin page.
-If the method returns an `HttpResponse`,
-then the user will see that response
-(which is what happens
-with the delete confirmation page
-of the delete action).
-Whatever you do between the method being called
-and the method returning
-is up to you.
-
-Maybe our sample book application
-could set a book
-to premiere
-on the site
-as an important new available title.
-In this hypothetical scenario,
-we might have code
-that unsets any older premiere book
-or sends out emails
-to people who have expressed interest
-when new premieres are announced.
-
-For this scenario,
-we could add an action
-that would do these things.
+The queryset will represent the set of model records that the user selected. If the method returns `None`, then the user will be returned to the same admin page. If the method returns an `HttpResponse`, then the user will see that response (which is what happens with the delete confirmation page of the delete action). Whatever you do between the method being called and the method returning is up to you.
+
+Maybe our sample book application could set a book to premiere on the site as an important new available title. In this hypothetical scenario, we might have code that unsets any older premiere book or sends out emails to people who have expressed interest when new premieres are announced.
+
+For this scenario, we could add an action that would do these things.
```python
# application/admin.py
@@ -930,25 +472,9 @@ class BookAdmin(admin.ModelAdmin):
update_premiere(book)
```
-Django will use the name
-of the method
-to set the label
-for the dropdown
-on the list page.
-In this case,
-the action label will be "Set premiere".
-
-We were able to extend the admin
-and hook into the page's user interface
-by defining a method
-and declaring it as an action.
-This is a powerful system
-to give administrators control
-and allow them
-to operate
-in custom ways
-on the data
-in their applications.
+Django will use the name of the method to set the label for the dropdown on the list page. In this case, the action label will be "Set premiere".
+
+We were able to extend the admin and hook into the page's user interface by defining a method and declaring it as an action. This is a powerful system to give administrators control and allow them to operate in custom ways on the data in their applications.
## Summary
@@ -958,24 +484,14 @@ In this article,
{{< book >}}
In this chapter,
{{< /book >}}
-we looked
-at the built-in Django administrator's site.
-This powerful extension gives us the ability
-to create, view, edit, and delete rows
-from database tables associated
-with your application's models.
+we looked at the built-in Django administrator's site. This powerful extension gives us the ability to create, view, edit, and delete rows from database tables associated with your application's models.
We've covered:
-* What the Django admin site is
- and how to set it up
+* What the Django admin site is and how to set it up
* How to make your models appear in the admin
-* How to customize your admin pages quickly
- with options
- provided by the `ModelAdmin` class
-* How to create extra actions
- that enable you to do work
- on your model records
+* How to customize your admin pages quickly with options provided by the `ModelAdmin` class
+* How to create extra actions that enable you to do work on your model records
{{< web >}}
Next time we will cover
@@ -983,26 +499,13 @@ Next time we will cover
{{< book >}}
In the next chapter we will cover
{{< /book >}}
-the anatomy
-of a Django application.
-A Django project is composed
-of many applications.
-We will explore:
+the anatomy of a Django application. A Django project is composed of many applications. We will explore:
* The conventional structure of a Django app
* How Django identifies and loads applications
* Why applications are crucial for the Django ecosystem
{{< web >}}
-If you'd like to follow along
-with the series,
-please feel free to sign up
-for my newsletter
-where I announce all of my new content.
-If you have other questions,
-you can reach me online
-on Twitter
-where I am
-{{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
+If you'd like to follow along with the series, please feel free to sign up for my newsletter where I announce all of my new content. If you have other questions, you can reach me online on Twitter where I am {{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
{{< /web >}}
From d2b7ecfe26a756959f197bace1a540e09ce9637a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nazar=C3=A9=20da=20Piedade?=
Date: Fri, 5 May 2023 11:15:51 +0100
Subject: [PATCH 09/30] chore: format
`understand-django/2020-09-29-anatomy-of-an-application.pt.md`
---
...2020-09-29-anatomy-of-an-application.pt.md | 529 +++---------------
1 file changed, 86 insertions(+), 443 deletions(-)
diff --git a/content/understand-django/2020-09-29-anatomy-of-an-application.pt.md b/content/understand-django/2020-09-29-anatomy-of-an-application.pt.md
index 4598679c..17b5e889 100644
--- a/content/understand-django/2020-09-29-anatomy-of-an-application.pt.md
+++ b/content/understand-django/2020-09-29-anatomy-of-an-application.pt.md
@@ -1,12 +1,7 @@
---
title: "Anatomy Of An Application"
description: >-
- This article explores applications.
- Applications are core structural elements
- of a Django project.
- We will see the composition
- of an app
- and how to use them effectively.
+ This article explores applications. Applications are core structural elements of a Django project. We will see the composition of an app and how to use them effectively.
image: img/django.png
type: post
categories:
@@ -21,100 +16,43 @@ series: "Understand Django"
---
{{< web >}}
-In the previous
-[Understand Django]({{< ref "/understand-django/_index.md" >}})
-article,
-we got deep
-into the Django administrators site.
-We saw what the site was
-and how to configure and customize it.
-In this article,
+In the previous [Understand Django]({{< ref "/understand-django/_index.pt.md" >}}) article, we got deep into the Django administrators site. We saw what the site was and how to configure and customize it. In this article,
{{< /web >}}
{{< book >}}
In this chapter,
{{< /book >}}
-we will examine what goes into an application.
-Applications are core elements
-of a Django project.
+we will examine what goes into an application. Applications are core elements of a Django project.
-{{< understand-django-series "apps" >}}
+{{< understand-django-series-pt "apps" >}}
## What Is An Application?
-Before getting to what a Django application **is**,
-we probably need to start with what it **is not**
-because the terminology is confusing.
-In the world of web development,
-developers may call a website a "web application."
-
-In Django parlance,
-a "web application" is a Django *project*.
-All of the pieces
-that come together to make a website
-are a project.
-The primary components
-within the project
-are called *applications*.
-In other words,
-a Django project is built
-from one or more Django applications.
-
-This situation is quite similar
-to Python packages.
-The software industry
-often describes the software unit
-as a "package."
-We think
-of `pip`,
-`npm`,
-or `apt`
-as "package" managers.
-This leads to a similar naming problem
-because Python also calls any directory
-with a `__init__.py` file a "package" as well.
-
-In reality,
-the code that you download
-using pip is technically called a "{{< extlink "https://packaging.python.org/overview/" "distribution" >}}."
-Even though we colloquially talk about Python downloads
-from PyPI
-(Python Package Index)
-as packages,
-we're really talking about distributions,
-and a distribution is a unit
-that contains one or more Python packages.
-
-Hopefully,
-you now understand
-the relationship
-of applications
-in Django.
+Before getting to what a Django application **is**, we probably need to start with what it **is not** because the terminology is confusing. In the world of web development, developers may call a website a "web application."
+
+In Django parlance, a "web application" is a Django *project*. All of the pieces that come together to make a website are a project. The primary components within the project are called *applications*. In other words, a Django project is built from one or more Django applications.
+
+This situation is quite similar to Python packages. The software industry often describes the software unit as a "package." We think of `pip`, `npm`, or `apt` as "package" managers. This leads to a similar naming problem because Python also calls any directory with a `__init__.py` file a "package" as well.
-> Your "web application" is a Django **project**
-composed of one or more Django **applications**.
+In reality, the code that you download using pip is technically called a "{{< extlink "https://packaging.python.org/overview/" "distribution" >}}." Even though we colloquially talk about Python downloads from PyPI (Python Package Index) as packages, we're really talking about distributions, and a distribution is a unit that contains one or more Python packages.
+
+Hopefully, you now understand the relationship of applications in Django.
+
+> Your "web application" is a Django **project** composed of one or more Django **applications**.
## Application Structure
-Let's look at a fully loaded Django application
-to see the fairly standard structure
-that you will encounter
-in Django projects.
+Let's look at a fully loaded Django application to see the fairly standard structure that you will encounter in Django projects.
-An application usually tries
-to capture a core concept
-within your system.
+An application usually tries to capture a core concept within your system.
{{< web >}}
For this article,
{{< /web >}}
{{< book >}}
For this chapter,
{{< /book >}}
-we will use movies
-as the concept we want to model.
+we will use movies as the concept we want to model.
-Let's see what a default scaffold includes,
-then build it up
-with all the extras.
+Let's see what a default scaffold includes, then build it up with all the extras:
```bash
(venv) $ ./manage.py startapp movies
@@ -130,20 +68,12 @@ movies
└── views.py
```
-`admin.py`:
-This file is where all your `ModelAdmin` classes live
-to power how the movies app will appear
-in the Django admin.
+`admin.py`: This file is where all your `ModelAdmin` classes live to power how the movies app will appear in the Django admin.
{{< web >}}
-You can learn more about the admin
-in [Administer All The Things]({{< ref "/understand-django/2020-08-26-administer-all-the-things.md" >}}).
+You can learn more about the admin in [Administer All The Things]({{< ref "/understand-django/2020-08-26-administer-all-the-things.pt.md" >}}).
{{< /web >}}
-`apps.py`:
-This file is for the `AppConfig`
-of the application.
-We will discuss the `AppConfig`
-and how to use it
+`apps.py`: This file is for the `AppConfig` of the application. We will discuss the `AppConfig` and how to use it
{{< web >}}
later in this article.
{{< /web >}}
@@ -151,120 +81,57 @@ later in this article.
later in this chapter.
{{< /book >}}
-`migrations`:
-This directory is where all database migrations are stored
-for the application.
-Any model changes for this app will generate a migration
-and create a numbered migration file
-in this directory.
+`migrations`: This directory is where all database migrations are stored for the application. Any model changes for this app will generate a migration and create a numbered migration file in this directory.
{{< web >}}
-More info about migrations is
-in [Store Data With Models]({{< ref "/understand-django/2020-06-25-store-data-with-models.md" >}}).
+More info about migrations is in [Store Data With Models]({{< ref "/understand-django/2020-06-25-store-data-with-models.pt.md" >}}).
{{< /web >}}
-`models.py`:
-This file is the home
-for all the Django `Model` classes
-in the application.
-The models represent all your database data.
+`models.py`: This file is the home for all the Django `Model` classes in the application. The models represent all your database data.
{{< web >}}
-Learn more about models
-in [Store Data With Models]({{< ref "/understand-django/2020-06-25-store-data-with-models.md" >}}).
+Learn more about models in [Store Data With Models]({{< ref "/understand-django/2020-06-25-store-data-with-models.pt.md" >}}).
{{< /web >}}
-`tests.py`:
-This file is for automated tests.
-We'll cover automated tests
-in Django
+`tests.py`: This file is for automated tests. We'll cover automated tests in Django
{{< web >}}
in a future article.
{{< /web >}}
{{< book >}}
in a future chapter.
{{< /book >}}
-For now,
-you can know that I *always* **delete** this file
-and replace it with a `tests` package.
-A `tests` package is superior
-because you can split out to more focused files
-like `test_models.py`
-to know where the appropriate tests are.
-
-`views.py`:
-This file is where Django view functions or classes go.
-Views are the glue code
-that connect your URL routes
-to your database models.
+For now, you can know that I *always* **delete** this file and replace it with a `tests` package. A `tests` package is superior because you can split out to more focused files like `test_models.py` to know where the appropriate tests are.
+
+`views.py`: This file is where Django view functions or classes go. Views are the glue code that connect your URL routes to your database models.
{{< web >}}
-I wrote about views
-in [Views On Views]({{< ref "/understand-django/2020-03-03-views-on-views.md" >}}).
+I wrote about views in [Views On Views]({{< ref "/understand-django/2020-03-03-views-on-views.pt.md" >}}).
{{< /web >}}
-That's everything
-that comes
-with a generated app,
-but what other files are missing
-that you will commonly see
-in a Django application?
-
-`urls.py`:
-This file is often used
-to create routes
-that logically group
-all movie related functionality.
-The `urls.py` file would power all the routes
-in something
-like `www.mysite.com/movies/`.
+That's everything that comes with a generated app, but what other files are missing that you will commonly see in a Django application?
+
+`urls.py`: This file is often used to create routes that logically group all movie related functionality. The `urls.py` file would power all the routes in something like `www.mysite.com/movies/`.
{{< web >}}
-Information
-on URLs is
-in [URLs Lead The Way]({{< ref "/understand-django/2020-01-22-urls-lead-way.md" >}}).
+Information on URLs is in [URLs Lead The Way]({{< ref "/understand-django/2020-01-22-urls-lead-way.pt.md" >}}).
{{< /web >}}
-`forms.py`:
-When you use Django `Form` classes
-to interact with users,
-this is the file
-where forms are stored.
+`forms.py`: When you use Django `Form` classes to interact with users, this is the file where forms are stored.
{{< web >}}
-You can discover more on forms
-in [User Interaction With Forms]({{< ref "/understand-django/2020-05-05-user-interaction-forms.md" >}}).
+You can discover more on forms in [User Interaction With Forms]({{< ref "/understand-django/2020-05-05-user-interaction-forms.pt.md" >}}).
{{< /web >}}
-`templatetags`:
-This directory is a Python package
-that would include a module
-like `movies_tags.py`
-where you'd define any custom template tags
-to use when rendering your templates.
+`templatetags`: This directory is a Python package that would include a module like `movies_tags.py` where you'd define any custom template tags to use when rendering your templates.
{{< web >}}
-Custom tags are a topic
-in [Templates For User Interfaces]({{< ref "/understand-django/2020-04-02-templates-user-interfaces.md" >}}).
+Custom tags are a topic in [Templates For User Interfaces]({{< ref "/understand-django/2020-04-02-templates-user-interfaces.pt.md" >}}).
{{< /web >}}
-`templates`:
-This directory can store templates
-that the application will render.
-I personally prefer
-using a project-wide `templates` directory
-as discussed
+`templates`: This directory can store templates that the application will render. I personally prefer using a project-wide `templates` directory as discussed
{{< web >}}
-in [Templates For User Interfaces]({{< ref "/understand-django/2020-04-02-templates-user-interfaces.md" >}}),
+in [Templates For User Interfaces]({{< ref "/understand-django/2020-04-02-templates-user-interfaces.pt.md" >}}),
{{< /web >}}
{{< book >}}
in the templates chapter,
{{< /book >}}
-but `templates` directories are commonly found
-in indvidual Django apps,
-especially for third party applications
-that you may pull into your project.
-
-`static`:
-For static files
-that you want to display,
-such as images,
-you can use the `static` directory.
-We'll discuss static files more
+but `templates` directories are commonly found in individual Django apps, especially for third party applications that you may pull into your project.
+
+`static`: For static files that you want to display, such as images, you can use the `static` directory. We'll discuss static files more
{{< web >}}
in a future article.
{{< /web >}}
@@ -272,13 +139,7 @@ in a future article.
in a future chapter.
{{< /book >}}
-`management`:
-Users can extend Django
-with custom commands
-that can be called via `manage.py`.
-Those commands are stored
-in this package.
-Custom commands are a future topic
+`management`: Users can extend Django with custom commands that can be called via `manage.py`. Those commands are stored in this package. Custom commands are a future topic
{{< web >}}
in this series.
{{< /web >}}
@@ -286,33 +147,14 @@ in this series.
in this book.
{{< /book >}}
-`locale`:
-When doing translations and internationalization,
-the translation files must have a home.
-That's the purpose
-of the `locale` directory.
-
-`managers.py`:
-This file is not always used,
-but if your application has a lot
-of custom managers,
-then you may want
-to separate them
-from your models
-in this file.
+`locale`: When doing translations and internationalization, the translation files must have a home. That's the purpose of the `locale` directory.
+
+`managers.py`: This file is not always used, but if your application has a lot of custom managers, then you may want to separate them from your models in this file.
{{< web >}}
-Managers are a topic
-in [Store Data With Models]({{< ref "/understand-django/2020-06-25-store-data-with-models.md" >}}).
+Managers are a topic in [Store Data With Models]({{< ref "/understand-django/2020-06-25-store-data-with-models.pt.md" >}}).
{{< /web >}}
-Most applications will *not* have all
-of these pieces,
-but this should give you an idea
-of what things are when you are exploring
-Django apps in the wild
-on your own.
-Here's what our final sample tree
-would look like.
+Most applications will *not* have all of these pieces, but this should give you an idea of what things are when you are exploring Django apps in the wild on your own. Here's what our final sample tree would look like:
```bash
(venv) $ tree movies
@@ -356,53 +198,15 @@ movies
## Loading applications
-We've now seen
-what's in a Django application
-and have an idea
-of an app's composition.
-How does Django load applications?
-
-Django does *not* do automatic discovery
-of Django applications
-within your project.
-If you want Django
-to include an app
-in your project,
-you *must* add the app
-to your `INSTALLED_APPS` list
-in the settings file.
-
-This is a good example
-of Django following the Python ethos
-of favoring explicit
-over implicit.
-By being explicit,
-your project is not in danger
-of including apps
-that you don't expect.
-That might seem silly
-for apps that you write yourself,
-but you'll be thankful
-that's the case when some third party package
-in your virtual environment
-happens to have a Django app
-that you don't want
-in your project.
-
-On startup,
-when an application is
-in `INSTALLED_APPS`,
-Django will look
-for an `AppConfig` class.
-This class is stored
-in `apps.py`
-from the `startapp` command
-and contains metadata
-about the application.
-
-When Django starts,
-it will initialize the system
-by doing the following:
+We've now seen what's in a Django application and have an idea of an app's composition. How does Django load applications?
+
+Django does *not* do automatic discovery of Django applications within your project. If you want Django to include an app in your project, you *must* add the app to your `INSTALLED_APPS` list in the settings file.
+
+This is a good example of Django following the Python ethos of favoring explicit over implicit. By being explicit, your project is not in danger of including apps that you don't expect. That might seem silly for apps that you write yourself, but you'll be thankful that's the case when some third party package in your virtual environment happens to have a Django app that you don't want in your project.
+
+On startup, when an application is in `INSTALLED_APPS`, Django will look for an `AppConfig` class. This class is stored in `apps.py` from the `startapp` command and contains metadata about the application.
+
+When Django starts, it will initialize the system by doing the following:
* Load the settings
* Configure logging (a topic we'll explore in the future)
@@ -411,185 +215,34 @@ by doing the following:
* Import a models module for each application
* Invoke the `ready` method of every `AppConfig` discovered
-The `ready` method is a useful hook
-for taking action
-at startup.
-Since models are already loaded
-by the time the method is called,
-it's a safe place
-to interact with Django.
-
-If you attempt to run setup code
-before Django is ready,
-and you try to do something
-like use the ORM
-to interact with database data,
-you'll probably get an `AppRegistryNotReady` exception.
-Most apps won't directly need to run startup code,
-but knowing about the `ready` hook
-is a useful piece
-of knowledge
-to keep in your back pocket.
+The `ready` method is a useful hook for taking action at startup. Since models are already loaded by the time the method is called, it's a safe place to interact with Django.
+
+If you attempt to run setup code before Django is ready, and you try to do something like use the ORM to interact with database data, you'll probably get an `AppRegistryNotReady` exception. Most apps won't directly need to run startup code, but knowing about the `ready` hook is a useful piece of knowledge to keep in your back pocket.
## Ecosystem Applications
-An application is an important tool
-for grouping the different logical components
-in your project,
-but apps also serve another purpose.
-Apps are the basis
-for most of the 3rd party extensions
-in the Django ecosystem.
-
-A big reason to use Django is
-that the framework takes a "batteries included" approach.
-Most of the tools
-that you need to build a website are baked directly
-into the framework.
-This is a vastly different approach compared
-to {{< extlink "https://flask.palletsprojects.com/en/2.2.x/" "Flask" >}}
-which provides a relatively small API
-and depends heavily
-on third party libraries.
-
-Even though Django includes most
-of the major pieces
-for a web application,
-the framework doesn't include *everything*.
-When you want to include more features,
-Django apps fill in the gaps.
-
-Before you go out to PyPI,
-we need look no further
-than the `django.contrib` package,
-a collection of "contributed" applications provided
-by Django itself.
-When you run the `startproject` command,
-Django will include a variety
-of built-in applications
-that perform different functions.
-If you don't need some of the functionality,
-you can opt out
-by removing the app
-from your list
-in `INSTALLED_APPS`.
-
-I think this is the big difference
-in philosophy
-behind the framework.
-Some developers like to start
-from an extremely minimal kernel
-of functionality
-and build it up based on their needs.
-Django's philosophy seems to be
-that you start
-with an opinionated baseline
-and pare down what you don't require.
-Django doesn't expect that you'll use every feature
-in every app,
-but many of the features that you'll want
-are at the ready
-when you need them.
-
-From my point of view,
-I think the Django philosophy is the right one
-(shocking, isn't it!? 🤪).
-The benefit of the Django philosophy is
-that you leverage the knowledge
-of people who have built web apps
-for a very long time.
-Not only do you leverage that knowledge,
-you benefit from the polishing
-applied by the Django developers
-to integrate the different major systems
-into a consistent whole.
-What you're left with is a framework
-that feels like it belongs together,
-and I think that is a positive impact
-on your productivity.
-
-When you build from a minimal kernel
-and work up,
-you depend on knowing everything
-that's required to put something
-on the web.
-That means that you know all the pieces
-and how to bolt them together.
-But most people *don't* know all the pieces
-(because there are so many!).
-
-If you start minimally
-and don't know the pieces,
-you'll learn along the way,
-but what happens when you encounter a new concept
-that doesn't fit
-into your original mental model?
-For instance,
-security is a critical part
-that can destroy your mental model
-when you learn
-of a class of vulnerabilities
-that can restrict what is possible
-to do safely.
-When you follow this building from scratch approach,
-I think the final result will naturally be your own custom framework.
-If that's your thing, awesome.
-Go for it.
-For me,
-I want a framework
-that is a commodity
-and commonly understood
-by many people.
-
-Ok, so, what does this have to do with Django apps?
-Apps are contained and reusable modules.
-Because they have a fairly standard structure,
-a project can integrate a new app quickly.
-This means you can leverage the knowledge
-and experience
-(read: battle scars)
-of other web developers.
-The apps all play
-by the same rules
-so you,
-as the developer,
-spend less time gluing the app
-into your project
-and more time benefiting
-from what it does.
-
-I think this standard structure also makes it easier
-to experiment
-with new apps.
-When I need some new functionality,
-I will often check
-{{< extlink "https://djangopackages.org/" "Django Packages" >}}
-to look
-for apps
-that could meet my needs.
-In my experience,
-adding a new app is,
-in many cases,
-little more
-than installing the package,
-adding the app
-to the `INSTALLED_APPS` list,
-and putting an `include`
-in my `urls.py` file.
-Some packages require more configuration
-than that,
-but I think
-that the integration cost is low enough
-for me to experiment rapidly
-and back out my decision
-if I discover
-that an app won't do
-what I need.
-
-All in all,
-Django applications make working
-with the Django ecosystem
-a more enjoyable experience.
+An application is an important tool for grouping the different logical components in your project, but apps also serve another purpose. Apps are the basis for most of the 3rd party extensions in the Django ecosystem.
+
+A big reason to use Django is that the framework takes a "batteries included" approach. Most of the tools that you need to build a website are baked directly into the framework. This is a vastly different approach compared to {{< extlink "https://flask.palletsprojects.com/en/2.2.x/" "Flask" >}}
+which provides a relatively small API and depends heavily on third party libraries.
+
+Even though Django includes most of the major pieces for a web application, the framework doesn't include *everything*. When you want to include more features, Django apps fill in the gaps.
+
+Before you go out to PyPI, we need look no further than the `django.contrib` package, a collection of "contributed" applications provided by Django itself. When you run the `startproject` command, Django will include a variety of built-in applications that perform different functions. If you don't need some of the functionality, you can opt out by removing the app from your list in `INSTALLED_APPS`.
+
+I think this is the big difference in philosophy behind the framework. Some developers like to start from an extremely minimal kernel of functionality and build it up based on their needs. Django's philosophy seems to be that you start with an opinionated baseline and pare down what you don't require. Django doesn't expect that you'll use every feature in every app, but many of the features that you'll want are at the ready when you need them.
+
+From my point of view, I think the Django philosophy is the right one (shocking, isn't it!? 🤪). The benefit of the Django philosophy is that you leverage the knowledge of people who have built web apps for a very long time. Not only do you leverage that knowledge, you benefit from the polishing applied by the Django developers to integrate the different major systems into a consistent whole. What you're left with is a framework that feels like it belongs together, and I think that is a positive impact on your productivity.
+
+When you build from a minimal kernel and work up, you depend on knowing everything that's required to put something on the web. That means that you know all the pieces and how to bolt them together. But most people *don't* know all the pieces (because there are so many!).
+
+If you start minimally and don't know the pieces, you'll learn along the way, but what happens when you encounter a new concept that doesn't fit into your original mental model? For instance, security is a critical part that can destroy your mental model when you learn of a class of vulnerabilities that can restrict what is possible to do safely. When you follow this building from scratch approach, I think the final result will naturally be your own custom framework. If that's your thing, awesome. Go for it. For me, I want a framework that is a commodity and commonly understood by many people.
+
+Ok, so, what does this have to do with Django apps? Apps are contained and reusable modules. Because they have a fairly standard structure, a project can integrate a new app quickly. This means you can leverage the knowledge and experience (read: battle scars) of other web developers. The apps all play by the same rules so you, as the developer, spend less time gluing the app into your project and more time benefiting from what it does.
+
+I think this standard structure also makes it easier to experiment with new apps. When I need some new functionality, I will often check {{< extlink "https://djangopackages.org/" "Django Packages" >}} to look for apps that could meet my needs. In my experience, adding a new app is, in many cases, little more than installing the package, adding the app to the `INSTALLED_APPS` list, and putting an `include` in my `urls.py` file. Some packages require more configuration than that, but I think that the integration cost is low enough for me to experiment rapidly and back out my decision if I discover that an app won't do what I need.
+
+All in all, Django applications make working with the Django ecosystem a more enjoyable experience.
## Summary
@@ -606,9 +259,7 @@ We saw:
* What a Django application is
* How a Django application is structured
-* How the Django ecosystem benefits
- from a common format
- that creates reusable components
+* How the Django ecosystem benefits from a common format that creates reusable components
{{< web >}}
Next time we will look into authentication
@@ -617,6 +268,7 @@ Next time we will look into authentication
Next, we will look into authentication
{{< /book >}}
in Django.
+
We will study:
* How users are created and managed
@@ -624,15 +276,6 @@ We will study:
* How to work with users in your views and templates
{{< web >}}
-If you'd like to follow along
-with the series,
-please feel free to sign up
-for my newsletter
-where I announce all of my new content.
-If you have other questions,
-you can reach me online
-on Twitter
-where I am
-{{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
+If you'd like to follow along with the series, please feel free to sign up for my newsletter where I announce all of my new content. If you have other questions, you can reach me online on Twitter where I am {{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
{{< /web >}}
From 0473f4a084321516c0d015ef66034f0f80c30fd6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nazar=C3=A9=20da=20Piedade?=
Date: Fri, 5 May 2023 11:16:32 +0100
Subject: [PATCH 10/30] chore: format
`understand-django/2020-11-04-user-authentication.pt.md`
---
.../2020-11-04-user-authentication.pt.md | 803 +++---------------
1 file changed, 135 insertions(+), 668 deletions(-)
diff --git a/content/understand-django/2020-11-04-user-authentication.pt.md b/content/understand-django/2020-11-04-user-authentication.pt.md
index 6177ca9a..9f0aaafb 100644
--- a/content/understand-django/2020-11-04-user-authentication.pt.md
+++ b/content/understand-django/2020-11-04-user-authentication.pt.md
@@ -1,10 +1,7 @@
---
title: "User Authentication"
description: >-
- Our focus in this Understand Django article
- is how to manage users
- in your Django application.
- We'll study Django's built-in user authentication system.
+ Our focus in this Understand Django article is how to manage users in your Django application. We'll study Django's built-in user authentication system.
image: img/django.png
type: post
categories:
@@ -20,83 +17,34 @@ series: "Understand Django"
---
{{< web >}}
-In the previous
-[Understand Django]({{< ref "/understand-django/_index.md" >}})
-article,
-we learned about the structure
-of a Django *application*
-and how apps are the core components
-of a Django project.
-In this article,
+In the previous [Understand Django]({{< ref "/understand-django/_index.pt.md" >}}) article, we learned about the structure of a Django *application* and how apps are the core components of a Django project. In this article,
{{< /web >}}
{{< book >}}
In this chapter,
{{< /book >}}
-we're going to dig into Django's built-in user authentication system.
-We'll see how Django makes your life easier
-by giving you tools
-to help your web application interact
-with the users
-of your site.
+we're going to dig into Django's built-in user authentication system. We'll see how Django makes your life easier by giving you tools to help your web application interact with the users of your site.
-{{< understand-django-series "auth" >}}
+{{< understand-django-series-pt "auth" >}}
## Authentication And Authorization
-We need to start with some terms
-before we begin our study.
-When your project interacts
-with users,
-there are two primary aspects tightly coupled
-to users
-that we must consider.
-
-*Authentication*:
-When a user tries to prove
-that they are who they say they are,
-that is authentication.
-A user will typically authenticate
-with your site
-via some login form
-or using a social provider
-like Google
-to verify their identity.
-
-> Authentication can only prove
-that {{< extlink "https://en.wikipedia.org/wiki/The_Important_Book" "you are you" >}}.
-
-*Authorization*:
-What is a user allowed to do?
-Authorization answers that question.
-We use authorization
-to determine what permissions or groups a user belongs to,
-so that we can scope what a user can do
-on the site.
+We need to start with some terms before we begin our study. When your project interacts with users, there are two primary aspects tightly coupled to users that we must consider.
+
+*Authentication*: When a user tries to prove that they are who they say they are, that is authentication. A user will typically authenticate with your site via some login form or using a social provider like Google to verify their identity.
+
+> Authentication can only prove that {{< extlink "https://en.wikipedia.org/wiki/The_Important_Book" "you are you" >}}.
+
+*Authorization*: What is a user allowed to do? Authorization answers that question. We use authorization to determine what permissions or groups a user belongs to, so that we can scope what a user can do on the site.
> Authorization determines what you can do.
-The Django auth system covers both of these topics.
-Sometimes the software industry will shorten authentication
-to "authn"
-and authorization to "authz,"
-but I think those labels are fairly silly
-and confusing.
-I will call out topics by their full name
-and refer to the entire Django system as "auth."
+The Django auth system covers both of these topics. Sometimes the software industry will shorten authentication to "authn" and authorization to "authz," but I think those labels are fairly silly and confusing. I will call out topics by their full name and refer to the entire Django system as "auth."
## Setup
-If you used the `startproject` command
-to begin your project,
-then, congratulations,
-you're done
-and can move on!
+If you used the `startproject` command to begin your project, then, congratulations, you're done and can move on!
-The auth features
-in Django require a couple
-of built-in Django applications
-and a couple
-of middleware classes.
+The auth features in Django require a couple of built-in Django applications and a couple of middleware classes.
The Django apps are:
@@ -108,260 +56,84 @@ The middleware classes are:
* `SessionMiddleware` to store data about a user in a session
* `AuthenticationMiddleware` to associate users with requests
-Middleware and sessions are both future topics
-so consider them internal details
-that you can ignore for now.
+Middleware and sessions are both future topics so consider them internal details that you can ignore for now.
-The Django docs provide additional context
-about these prerequisites
-so check out the
-{{< extlink "https://docs.djangoproject.com/en/4.1/topics/auth/#installation" "auth topic installation section" >}}
-for more details.
+The Django docs provide additional context about these prerequisites so check out the {{< extlink "https://docs.djangoproject.com/en/4.1/topics/auth/#installation" "auth topic installation section" >}} for more details.
## Who Authenticates?
-If your site is going to have any level
-of personalization
-for anyone
-who uses it,
-then we need some way
-to track identity.
-
-In the Django auth system,
-identity is tracked
-with a `User` model.
-This model stores information
-that you'd likely want
-to associate
-with anyone who uses your site.
-The model includes:
+If your site is going to have any level of personalization for anyone who uses it, then we need some way to track identity.
+
+In the Django auth system, identity is tracked with a `User` model. This model stores information that you'd likely want to associate with anyone who uses your site. The model includes:
* name fields,
* email address,
-* datetime fields
- for when a user joins
- or logs in to your site,
-* boolean fields
- for some coarse permissions
- that are very commonly needed,
+* datetime fields for when a user joins or logs in to your site,
+* boolean fields for some coarse permissions that are very commonly needed,
* and password data.
-The `User` model is a critically important model
-in many systems.
-Unless you're creating a website
-that is entirely public data
-and has no need
-to factor in identity,
-then you will probably use the `User` model heavily.
-
-Even if you *don't* expect your site's visitors
-to identify
-in some fashion,
-you'll still probably benefit
-from the `User` model
-because it is integrated
-with the Django admin site.
-I mentioned
+The `User` model is a critically important model in many systems. Unless you're creating a website that is entirely public data and has no need to factor in identity, then you will probably use the `User` model heavily.
+
+Even if you *don't* expect your site's visitors to identify in some fashion, you'll still probably benefit from the `User` model because it is integrated with the Django admin site. I mentioned
{{< web >}}
-in [Administer All The Things]({{< ref "/understand-django/2020-08-26-administer-all-the-things.md" >}})
+in [Administer All The Things]({{< ref "/understand-django/2020-08-26-administer-all-the-things.pt.md" >}})
{{< /web >}}
{{< book >}}
in the Administer All The Things chapter
{{< /book >}}
-that we needed a user
-with certain permissions
-to access the admin,
-but we glossed over the details
-of what that meant.
-
-The admin will only permit users
-with the `is_staff` attribute set
-to `True`.
-`is_staff` is one of the boolean fields
-that I listed
-as included
-in the default `User` model implementation.
-
-Now we have an understanding
-that the `User` model is a very important model
-in a Django site.
-At minimum,
-the model is important as you use the Django admin,
-but it can also be very important
-for the people
-that come to your site.
-
-Next,
-let's look a bit deeper
-at authentication
-and how that works
-in conjunction
-with the `User` model.
+that we needed a user with certain permissions to access the admin, but we glossed over the details of what that meant.
+
+The admin will only permit users with the `is_staff` attribute set to `True`. `is_staff` is one of the boolean fields that I listed as included in the default `User` model implementation.
+
+Now we have an understanding that the `User` model is a very important model in a Django site. At minimum, the model is important as you use the Django admin, but it can also be very important for the people that come to your site.
+
+Next, let's look a bit deeper at authentication and how that works in conjunction with the `User` model.
## Authenticating With Passwords
-Like many other websites
-that you've used,
-Django's built-in auth system
-authenticates users
-with passwords.
-
-When a user wants to authenticate,
-the user must log in
-to the site.
-Django includes a `LoginView` class-based view
-that can handle the appropriate steps.
-The `LoginView` is a form view that:
-
-* Collects the `username` and `password`
- from the user
-* Calls the `django.contrib.auth.authenticate` function
- with the `username` and `password`
- to confirm that the user is who they claim to be
-* Redirects to either a path that is set
- as the value of the `next` parameter
- in the URL's querystring
- or `settings.LOGIN_REDIRECT_URL`
- if the `next` parameter isn't set
-* Or, if authentication failed,
- re-renders the form page
- with appropriate error messages
-
-How does the `authenticate` function work?
-The `authenticate` function delegates the responsibility
-of deciding
-if the user's credentials are valid
-to an *authentication backend*.
-
-Like we have seen
-with templates
-and with databases,
-the auth system has swappable backends.
-With different backend options,
-you can have multiple ways
-of authenticating.
-The `authenticate` function will loop through any auth backends
-that are set
-in the `AUTHENTICATION_BACKENDS` list setting.
-Each backend can do one of three things:
+Like many other websites that you've used, Django's built-in auth system authenticates users with passwords.
+
+When a user wants to authenticate, the user must log in to the site. Django includes a `LoginView` class-based view that can handle the appropriate steps. The `LoginView` is a form view that:
+
+* Collects the `username` and `password` from the user
+* Calls the `django.contrib.auth.authenticate` function with the `username` and `password` to confirm that the user is who they claim to be
+* Redirects to either a path that is set as the value of the `next` parameter in the URL's querystring or `settings.LOGIN_REDIRECT_URL` if the `next` parameter isn't set
+* Or, if authentication failed, re-renders the form page with appropriate error messages
+
+How does the `authenticate` function work? The `authenticate` function delegates the responsibility of deciding if the user's credentials are valid to an *authentication backend*.
+
+Like we have seen with templates and with databases, the auth system has swappable backends. With different backend options, you can have multiple ways of authenticating. The `authenticate` function will loop through any auth backends that are set in the `AUTHENTICATION_BACKENDS` list setting. Each backend can do one of three things:
* Authenticate correctly with the user and return a `User` instance.
-* Not authenticate and return `None`.
- In this case, the next backend is tried.
-* Not authenticate and raise a `PermissionDenied` exception.
- In this case, no other backends are tried.
-
-You could add a backend
-to that setting
-that lets people authenticate
-with their social media accounts
-({{< extlink "https://django-allauth.readthedocs.io/en/latest/" "django-allauth" >}}
-is a great option to do exactly that).
-You might be in a corporate setting
-and need Single Sign-On (SSO)
-for your company.
-There are backend options
-that enable that too.
-
-Although there are many options,
-we'll focus
-on the built-in backend
-included with the auth system.
-The default backend is called the `ModelBackend`
-and it is in the `django.contrib.auth.backends` module.
-
-The `ModelBackend` is named as it is
-because it uses the `User` model
-to authenticate.
-Given a `username` and `password`
-from the user,
-the backend compares the provided data
-to any existing `User` records.
-
-The `authenticate` function calls the `authenticate` *method*
-that exists on the `ModelBackend`.
-The backend does a lookup
-of a `User` record
-based on the given `username` passed to the method
-by the `authenticate` function.
-If the user record exists,
-the backend calls `user.check_password(password)`
-where `password` is the actual password
-that is supplied
-by the person
-who submitted the POST
-to the `LoginView`.
-
-Django doesn't store actual passwords.
-To do so would be a major weakness
-in the framework
-because any leak
-of the database would leak all users' passwords.
-And that's totally not cool.
-Instead,
-the `password` field
-on the `User` model
-stores a *hash*
-of the password.
-
-Maybe you've never encountered hashing before.
-A hash is a computed value
-that is generated
-by running input data through a special function.
-The details of the computation is a very deep topic,
-especially when considering security,
-but the important thing
-to know about hashes is that you can't reverse the computation.
-
-In other words,
-if you generated a hash
-from `mysekretpassword`,
-then you wouldn't be able
-to take the hash value
-and figure out that the original input was `myseckretpassword`.
-
-Why is this useful?
-By computing hashes,
-Django can safely store
-that computed value
-without compromising a user's password.
-When a user wants to authenticate
-with a site,
-the user submits a password,
-Django computes the hash
-on that submitted value
-and *compares it to the hash stored
-in the database.*
-If the hashes match,
-then the site can conclude
-that the user sent the correct password.
-Only the password's hash
-would match the hash stored
-in the `User` model.
-
-Hashing is a fascinating subject.
-If you want to learn more about the guts
-of how Django manages hashes,
-I would suggest reading the
-{{< extlink "https://docs.djangoproject.com/en/4.1/topics/auth/passwords/" "Password management in Django" >}}
-docs
-to see the details.
+* Not authenticate and return `None`. In this case, the next backend is tried.
+* Not authenticate and raise a `PermissionDenied` exception. In this case, no other backends are tried.
+
+You could add a backend to that setting that lets people authenticate with their social media accounts ({{< extlink "https://django-allauth.readthedocs.io/en/latest/" "django-allauth" >}}
+is a great option to do exactly that). You might be in a corporate setting and need Single Sign-On (SSO) for your company. There are backend options that enable that too.
+
+Although there are many options, we'll focus on the built-in backend included with the auth system. The default backend is called the `ModelBackend` and it is in the `django.contrib.auth.backends` module.
+
+The `ModelBackend` is named as it is because it uses the `User` model to authenticate. Given a `username` and `password` from the user, the backend compares the provided data to any existing `User` records.
+
+The `authenticate` function calls the `authenticate` *method* that exists on the `ModelBackend`. The backend does a lookup of a `User` record based on the given `username` passed to the method by the `authenticate` function. If the user record exists, the backend calls `user.check_password(password)` where `password` is the actual password that is supplied by the person who submitted the POST to the `LoginView`.
+
+Django doesn't store actual passwords. To do so would be a major weakness in the framework because any leak of the database would leak all users' passwords. And that's totally not cool. Instead, the `password` field on the `User` model stores a *hash* of the password.
+
+Maybe you've never encountered hashing before. A hash is a computed value that is generated by running input data through a special function. The details of the computation is a very deep topic, especially when considering security, but the important thing to know about hashes is that you can't reverse the computation.
+
+In other words, if you generated a hash from `mysekretpassword`, then you wouldn't be able to take the hash value and figure out that the original input was `myseckretpassword`.
+
+Why is this useful? By computing hashes, Django can safely store that computed value without compromising a user's password. When a user wants to authenticate with a site, the user submits a password, Django computes the hash on that submitted value and *compares it to the hash stored in the database.* If the hashes match, then the site can conclude that the user sent the correct password. Only the password's hash would match the hash stored in the `User` model.
+
+Hashing is a fascinating subject. If you want to learn more about the guts of how Django manages hashes, I would suggest reading the {{< extlink "https://docs.djangoproject.com/en/4.1/topics/auth/passwords/" "Password management in Django" >}} docs to see the details.
## Authentication Views
-That's a lot of stuff
-to do for authentication!
+That's a lot of stuff to do for authentication!
-Is Django going to expect you to call the `authenticate` function
-and wire together all the views yourself?
-No!
+Is Django going to expect you to call the `authenticate` function and wire together all the views yourself? No!
-I mentioned the `LoginView` earlier,
-but that's not the only view
-that Django provides
-to make authentication manageable.
-You can add the set of views
-with a single `include`:
+I mentioned the `LoginView` earlier, but that's not the only view that Django provides to make authentication manageable. You can add the set of views with a single `include`:
```python
# project/urls.py
@@ -384,133 +156,33 @@ This set includes a variety of features.
* Views to change a password
* Views to reset a password
-If you choose to add this set,
-your job is to override the built-in templates
-to match the styling
-of your site.
-For example,
-to customize the logout view,
-you would create a file named `registration/logged_out.html`
-in your templates directory.
-The {{< extlink "https://docs.djangoproject.com/en/4.1/topics/auth/default/#all-authentication-views" "All authentication views" >}}
-documentation provides information
-about each view
-and the name of each template
-to override.
-Note that you *must* provide a template for the login view
-as the framework does not supply a default template for that view.
-
-If you have more complex needs
-for your site,
-you might want to consider some external Django applications
-that exist in the ecosystem.
-I personally like {{< extlink "https://django-allauth.readthedocs.io/en/latest/" "django-allauth" >}}.
-The project is very customizable
-and provides a path
-to add social authentication
-to sign up with your social media platform
-of choice.
-I also like django-allauth
-because it includes sign up flows
-that you don't have to build yourself.
-The application is definitely worth checking out.
-
-We've now seen how Django authenticates users
-to a website
-with the `User` model,
-the `authenticate` function,
-and the built-in authentication backend, `ModelBackend`.
-We've also seen how Django provides views
-to assist
-with login, logout, and password management.
-
-Once a user is authenticated,
-what is that user allowed to do?
-We'll see that next as we explore authorization
-in Django.
+If you choose to add this set, your job is to override the built-in templates to match the styling of your site. For example, to customize the logout view, you would create a file named `registration/logged_out.html` in your templates directory. The {{< extlink "https://docs.djangoproject.com/en/4.1/topics/auth/default/#all-authentication-views" "All authentication views" >}} documentation provides information about each view and the name of each template to override. Note that you *must* provide a template for the login view as the framework does not supply a default template for that view.
+
+If you have more complex needs for your site, you might want to consider some external Django applications that exist in the ecosystem. I personally like {{< extlink "https://django-allauth.readthedocs.io/en/latest/" "django-allauth" >}}.
+The project is very customizable and provides a path to add social authentication to sign up with your social media platform of choice. I also like `django-allauth` because it includes sign up flows that you don't have to build yourself. The application is definitely worth checking out.
+
+We've now seen how Django authenticates users to a website with the `User` model, the `authenticate` function, and the built-in authentication backend, `ModelBackend`. We've also seen how Django provides views to assist with login, logout, and password management.
+
+Once a user is authenticated, what is that user allowed to do? We'll see that next as we explore authorization in Django.
## What's Allowed?
### Authorization From User Attributes
-Django has multiple ways
-to let you control what a user is allowed
-to do
-on your site.
-
-The simplest form
-of checking
-on a user
-is to check
-if the site has identified the user or not.
-Before a user is authenticated
-by logging in,
-that user is anonymous.
-In fact,
-the Django auth system has a special class
-to represent this kind
-of anonymous user.
-Sticking to the principle
-of least surprise,
-the class is called `AnonymousUser`.
-
-The `User` model includes an `is_authenticated` attribute.
-Predictably,
-users that have authenticated will return `True`
-for `is_authenticated`
-while `AnonymousUser` instances return `False`
-for the same attribute.
-
-Django provides a `login_required` decorator
-that can use this `is_authenticated` information.
-The decorator will gate any view
-that needs a user to be authenticated.
-
-This may be the appropriate level
-of authorization check
-if you have an application
-that restricts who is allowed
-to log in.
-For instance,
-if you're running a Software as a Service (SaaS) application
-that requires users
-to pay a subscription
-to use the product,
-then you may have sufficient authorization checking
-by checking `is_authenticated`.
-In that scenario,
-if your application only permits users
-with an active subscription
-(or a trial subscription)
-to log in,
-`login_required` will guard against any non-paying users
-from using your product.
-
-There are other boolean values
-on the `User` model
-that you can use for authorization checking.
-
-* `is_staff` is a boolean to decide
- whether a user is a staff member or not.
- By default,
- this boolean is `False`.
- Only staff-level users are allowed
- to use the built-in Django admin site.
- You can also use the `staff_member_required` decorator
- if you have views that should only be used
- by members of your team
- with that permission.
-* `is_superuser` is a special flag
- to indicate a user
- that should have access to everything.
- This "superuser" concept is very similar
- to the superuser that is present
- in Linux permission systems.
- There's no special decorator
- for this boolean,
- but you could use the `user_passes_test` decorator
- if you had very private views
- that you needed to protect.
+Django has multiple ways to let you control what a user is allowed to do on your site.
+
+The simplest form of checking on a user is to check if the site has identified the user or not. Before a user is authenticated by logging in, that user is anonymous. In fact, the Django auth system has a special class to represent this kind of anonymous user. Sticking to the principle of least surprise, the class is called `AnonymousUser`.
+
+The `User` model includes an `is_authenticated` attribute. Predictably, users that have authenticated will return `True` for `is_authenticated` while `AnonymousUser` instances return `False` for the same attribute.
+
+Django provides a `login_required` decorator that can use this `is_authenticated` information. The decorator will gate any view that needs a user to be authenticated.
+
+This may be the appropriate level of authorization check if you have an application that restricts who is allowed to log in. For instance, if you're running a Software as a Service (SaaS) application that requires users to pay a subscription to use the product, then you may have sufficient authorization checking by checking `is_authenticated`. In that scenario, if your application only permits users with an active subscription (or a trial subscription) to log in, `login_required` will guard against any non-paying users from using your product.
+
+There are other boolean values on the `User` model that you can use for authorization checking:
+
+* `is_staff` is a boolean to decide whether a user is a staff member or not. By default, this boolean is `False`. Only staff-level users are allowed to use the built-in Django admin site. You can also use the `staff_member_required` decorator if you have views that should only be used by members of your team with that permission.
+* `is_superuser` is a special flag to indicate a user that should have access to everything. This "superuser" concept is very similar to the superuser that is present in Linux permission systems. There's no special decorator for this boolean, but you could use the `user_passes_test` decorator if you had very private views that you needed to protect:
```python
from django.contrib.admin.views.decorators import (
@@ -537,101 +209,28 @@ def special_view(request):
)
```
-The `user_passes_test` decorator behaves much
-like `login_required`,
-but it accepts a callable
-that receives a user object
-and returns a boolean.
-If the boolean value is `True`,
-the request is permitted
-and the user gets the response.
-If the boolean value is `False`,
-the user will be redirected
-to the login page.
+The `user_passes_test` decorator behaves much like `login_required`, but it accepts a callable that receives a user object and returns a boolean. If the boolean value is `True`, the request is permitted and the user gets the response. If the boolean value is `False`, the user will be redirected to the login page.
### Authorization From Permissions And Groups
-The first set of checks
-that we looked at is data
-that is stored
-with a `User` model record.
-While that works well
-for some cases
-that apply to many sites,
-what about authorization
-that depends
-on what your application does?
-
-Django comes with a flexible permission system
-that can let your application control
-who can see what.
-The permission system includes some convenient auto-created permissions
-as well as the ability to make custom permission
-for whatever purpose.
-These permission records are `Permission` model instances
-from `django.contrib.auth.models`.
-
-Any time you create a new model,
-Django will create an additional set of permissions.
-These auto-created permissions map
-to the Create, Read, Update, and Delete (CRUD) operations
-that you can expect to use
-in the Django admin.
-For instance,
-if you have a `pizzas` app
-and create a `Topping` model,
-Django would create the following permissions:
+The first set of checks that we looked at is data that is stored with a `User` model record. While that works well for some cases that apply to many sites, what about authorization that depends on what your application does?
+
+Django comes with a flexible permission system that can let your application control who can see what. The permission system includes some convenient auto-created permissions as well as the ability to make custom permission for whatever purpose. These permission records are `Permission` model instances from `django.contrib.auth.models`.
+
+Any time you create a new model, Django will create an additional set of permissions. These auto-created permissions map to the Create, Read, Update, and Delete (CRUD) operations that you can expect to use in the Django admin. For instance, if you have a `pizzas` app and create a `Topping` model, Django would create the following permissions:
* `pizzas.add_topping` for Create
* `pizzas.view_topping` for Read
* `pizzas.change_topping` for Update
* `pizzas.delete_topping` for Delete
-A big reason to create these permissions is to aid your development
-*and* add control to the Django admin.
-Staff-level users (i.e., `user.is_staff == True`)
-in your application have no permissions
-to start with.
-This is a safe default
-so that any new staff member cannot access all
-of the data
-in your system
-unless you grant them more permissions
-as you gain trust
-in them.
-
-When a staff user logs into the Django admin,
-they will initially see very little.
-As permissions are granted
-to the user's account,
-the Django admin will reveal additional information corresponding
-to the selected permissions.
-Although permissions are often granted
-through the `User` admin page,
-you can add permissions
-to a user
-through code.
-The `User` model has a `ManyToManyField`
-called `user_permissions`
-that associates user instances
-to particular permissions.
-
-Continuing with the pizza application example,
-perhaps you work with a chef
-for your pizza app.
-Your chef may need the ability
-to control any new toppings
-that should be available to customers,
-but you probably don't want the chef
-to be able to delete orders
-from the application's history.
-
-For the chef,
-you'd grant the `pizzas.add_topping`,
-`pizzas.view_topping`,
-and
-`pizzas.change_topping` permissions,
-but you'd leave out `orders.delete_order`.
+A big reason to create these permissions is to aid your development *and* add control to the Django admin. Staff-level users (i.e., `user.is_staff == True`) in your application have no permissions to start with. This is a safe default so that any new staff member cannot access all of the data in your system unless you grant them more permissions as you gain trust in them.
+
+When a staff user logs into the Django admin, they will initially see very little. As permissions are granted to the user's account, the Django admin will reveal additional information corresponding to the selected permissions. Although permissions are often granted through the `User` admin page, you can add permissions to a user through code. The `User` model has a `ManyToManyField` called `user_permissions` that associates user instances to particular permissions.
+
+Continuing with the pizza application example, perhaps you work with a chef for your pizza app. Your chef may need the ability to control any new toppings that should be available to customers, but you probably don't want the chef to be able to delete orders from the application's history.
+
+For the chef, you'd grant the `pizzas.add_topping`, `pizzas.view_topping`, and `pizzas.change_topping` permissions, but you'd leave out `orders.delete_order`:
```python
from django.contrib.auth.models import (
@@ -654,59 +253,15 @@ chef = User.objects.get(id=42)
chef.user_permissions.add(permission)
```
-We haven't covered the `contenttypes` app
-so this code may look unusual
-to you,
-but the auth system uses content types
-as a way
-to reference models generically
-when handling permissions.
-You can learn more about content types
-and their uses
-at {{< extlink "https://docs.djangoproject.com/en/4.1/ref/contrib/contenttypes/" "the contenttypes framework" >}}
-documentation.
-The important point
-to observe from the example
-is that permissions behave
-like any other Django model.
-
-Adding permissions to individual users is a nice feature
-for a small team,
-but if your team grows,
-it could devolve
-into a nightmare.
-
-Let's suppose that your application is wildly successful,
-and you need to hire a large support staff
-to help
-with customer issues.
-If your support team needs
-to view certain models
-in your system,
-it would be a total pain
-if you had to manage that
-per staff member.
-
-Django has an ability to create groups
-to alleviate this problem.
-The `Group` model is the intersection
-between a set of permissions
-and a set of users.
-Thus,
-you could create a group
-like "Support Team,"
-assign all the permissions
-that such a team should have,
-and include all your support staff
-on that team.
-Now,
-any time that the support staff members require a new permission,
-it can be added once
-to the group.
-
-A user's groups are tracked
-with another `ManyToManyField`
-called `groups`.
+We haven't covered the `contenttypes` app so this code may look unusual to you, but the auth system uses content types as a way to reference models generically when handling permissions. You can learn more about content types and their uses at {{< extlink "https://docs.djangoproject.com/en/4.1/ref/contrib/contenttypes/" "the contenttypes framework" >}} documentation. The important point to observe from the example is that permissions behave like any other Django model.
+
+Adding permissions to individual users is a nice feature for a small team, but if your team grows, it could devolve into a nightmare.
+
+Let's suppose that your application is wildly successful, and you need to hire a large support staff to help with customer issues. If your support team needs to view certain models in your system, it would be a total pain if you had to manage that per staff member.
+
+Django has an ability to create groups to alleviate this problem. The `Group` model is the intersection between a set of permissions and a set of users. Thus, you could create a group like "Support Team," assign all the permissions that such a team should have, and include all your support staff on that team. Now, any time that the support staff members require a new permission, it can be added once to the group.
+
+A user's groups are tracked with another `ManyToManyField` called `groups`:
```python
from django.contrib.auth.models import (
@@ -722,15 +277,9 @@ support_sally = User.objects.get(
support_sally.groups.add(support_team)
```
-In addition to the built-in permissions
-that Django creates
-and the group management system,
-you can create additional permissions
-for your own purposes.
+In addition to the built-in permissions that Django creates and the group management system, you can create additional permissions for your own purposes.
-Let's give our chef permission
-to bake pizzas
-in our imaginary app.
+Let's give our chef permission to bake pizzas in our imaginary app:
```python
from django.contrib.auth.models import (
@@ -754,13 +303,7 @@ chef = User.objects.get(id=42)
chef.user_permissions.add(permission)
```
-To check on the permission in our code,
-you can use the `has_perm` method
-on the `User` model.
-`has_perm` expects an application label
-and the permission codename
-joined together
-by a period.
+To check on the permission in our code, you can use the `has_perm` method on the `User` model. `has_perm` expects an application label and the permission codename joined together by a period:
```python
>>> chef = User.objects.get(id=42)
@@ -768,11 +311,7 @@ by a period.
True
```
-You can also use a decorator
-on a view
-to check a permission as well.
-The decorator will check the `request.user`
-for the proper permission.
+You can also use a decorator on a view to check a permission as well. The decorator will check the `request.user` for the proper permission:
```python
# pizzas/views.py
@@ -788,22 +327,11 @@ def bake_pizza(request):
## Working With Users In Views And Templates
-Now we've discussed how to authenticate users
-and how to check their authorization.
-How do we *interact* with users
-in your application code?
+Now we've discussed how to authenticate users and how to check their authorization. How do we *interact* with users in your application code?
-The first way is inside of views.
-Part of configuring the auth system is to include the `AuthenticationMiddleware`
-in `django.contrib.auth.middleware`.
+The first way is inside of views. Part of configuring the auth system is to include the `AuthenticationMiddleware` in `django.contrib.auth.middleware`.
-This middleware has one job
-in request processing:
-add a `user` attribute to the `request`
-that the view will receive.
-This middleware gives us very clean
-and convenient access
-to the user record.
+This middleware has one job in request processing: add a `user` attribute to the `request` that the view will receive. This middleware gives us very clean and convenient access to the user record:
```python
# application/views.py
@@ -821,60 +349,24 @@ def my_view(request):
)
```
-The `AuthenticationMiddleware` is what makes it possible
-for the decorators
+The `AuthenticationMiddleware` is what makes it possible for the decorators
{{< web >}}
that I've described in this article
{{< /web >}}
{{< book >}}
that I've described in this chapter
{{< /book >}}
-(i.e., `login_required`, `user_passes_test`, and `permission_required`)
-to work.
-Each of the decorators finds the `user` record
-as an attribute attached to the `request`.
-
-How about templates?
-If you had to add a user
-to a view's context
-for every view,
-that would be tedious.
-
-Thankfully,
-there is a context processor
-named `auth`
-that lets you avoid that pain
-(the processor is in `django.contrib.auth.context_processors`).
-The context processor will add a `user`
-to the context
-of every view
-when processing a request.
-
-Recall that a context processor is a function
-that receives a `request` object
-and returns a dictionary
-that will be merged
-into the context.
-Knowing that,
-can you guess how this context processor works?
-
-If you guessed `AuthenticationMiddleware`,
-you get a cookie! 🍪
-Since the middleware adds the `user`
-to the `request`,
-the context processor has the very trivial job
-of creating a dictionary
-like `{'user': request.user}`.
-There's a bit more to the actual implementation,
-and you can check out the
-{{< extlink "https://github.com/django/django/blob/4.1/django/contrib/auth/context_processors.py#L49" "Django source code" >}}
-if you want to see those details.
-
-What does this look like in practice?
-We've actually seen this already!
-One of the examples from the explanation
-of templates used the `user` context variable.
-Here's the example again so you don't need to jump back.
+(i.e., `login_required`, `user_passes_test`, and `permission_required`) to work. Each of the decorators finds the `user` record as an attribute attached to the `request`.
+
+How about templates? If you had to add a user to a view's context for every view, that would be tedious.
+
+Thankfully, there is a context processor named `auth` that lets you avoid that pain (the processor is in `django.contrib.auth.context_processors`). The context processor will add a `user` to the context of every view when processing a request.
+
+Recall that a context processor is a function that receives a `request` object and returns a dictionary that will be merged into the context. Knowing that, can you guess how this context processor works?
+
+If you guessed `AuthenticationMiddleware`, you get a cookie! 🍪 Since the middleware adds the `user` to the `request`, the context processor has the very trivial job of creating a dictionary like `{'user': request.user}`. There's a bit more to the actual implementation, and you can check out the {{< extlink "https://github.com/django/django/blob/4.1/django/contrib/auth/context_processors.py#L49" "Django source code" >}} if you want to see those details.
+
+What does this look like in practice? We've actually seen this already! One of the examples from the explanation of templates used the `user` context variable. Here's the example again so you don't need to jump back:
{{< web >}}
```django
@@ -891,19 +383,9 @@ Here's the example again so you don't need to jump back.
```
{{< /book >}}
-If you decide to use Django's permissions,
-you can also take advantage of the `perms` context variable
-in your templates.
-This variable is supplied
-by the `auth` context processor as well
-and gives your template access to the permissions
-of the `user` in a concise manner.
-The {{< extlink "https://docs.djangoproject.com/en/4.1/topics/auth/default/#permissions" "Django docs" >}} include some good examples
-of how the `perms` variable can be used.
+If you decide to use Django's permissions, you can also take advantage of the `perms` context variable in your templates. This variable is supplied by the `auth` context processor as well and gives your template access to the permissions of the `user` in a concise manner. The {{< extlink "https://docs.djangoproject.com/en/4.1/topics/auth/default/#permissions" "Django docs" >}} include some good examples of how the `perms` variable can be used.
-Now you've seen how Django leverages the auth middleware
-to make users easily accessible
-to your views and templates.
+Now you've seen how Django leverages the auth middleware to make users easily accessible to your views and templates.
## Summary
@@ -930,28 +412,13 @@ Next time we'll study middleware
{{< book >}}
In the next chapter, we'll study middleware
{{< /book >}}
-in Django.
-As the name implies,
-middleware is some code
-that exists
-in the "middle"
-of the request and response process.
-We will learn about:
+in Django. As the name implies, middleware is some code that exists in the "middle" of the request and response process. We will learn about:
* The mental model for considering middleware
* How to write your own middleware
* Some of the middleware classes that come with Django
{{< web >}}
-If you'd like to follow along
-with the series,
-please feel free to sign up
-for my newsletter
-where I announce all of my new content.
-If you have other questions,
-you can reach me online
-on Twitter
-where I am
-{{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
+If you'd like to follow along with the series, please feel free to sign up for my newsletter where I announce all of my new content. If you have other questions, you can reach me online on Twitter where I am {{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
{{< /web >}}
From 8ecff8aef29f3eb74510a9ee2dc63ef451ed448b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nazar=C3=A9=20da=20Piedade?=
Date: Fri, 5 May 2023 11:17:01 +0100
Subject: [PATCH 11/30] chore: format
`understand-django/2020-12-16-middleware-do-you-go.pt.md`
---
.../2020-12-16-middleware-do-you-go.pt.md | 642 +++---------------
1 file changed, 103 insertions(+), 539 deletions(-)
diff --git a/content/understand-django/2020-12-16-middleware-do-you-go.pt.md b/content/understand-django/2020-12-16-middleware-do-you-go.pt.md
index 4545684a..0fe738a8 100644
--- a/content/understand-django/2020-12-16-middleware-do-you-go.pt.md
+++ b/content/understand-django/2020-12-16-middleware-do-you-go.pt.md
@@ -1,12 +1,7 @@
---
title: "Middleware Do You Go?"
description: >-
- The topic for this Understand Django article
- is middleware.
- We'll see what middleware is,
- what it is used for
- in a Django project,
- and how to write your own.
+ The topic for this Understand Django article is middleware. We'll see what middleware is, what it is used for in a Django project, and how to write your own.
image: img/django.png
type: post
categories:
@@ -21,92 +16,27 @@ series: "Understand Django"
---
{{< web >}}
-In the previous
-[Understand Django]({{< ref "/understand-django/_index.md" >}})
-article,
-we covered the built-in auth system.
-That article gave you a chance
-to see the `User` model,
-ways to login users
-with Django's authentication tools,
-and the features
-that make the authorization controls work.
-In that topic,
-middleware came up
-as an integral component.
-Now we're going to learn more
-about middleware
-and its function
-within a Django project.
+In the previous [Understand Django]({{< ref "/understand-django/_index.pt.md" >}}) article, we covered the built-in auth system. That article gave you a chance to see the `User` model, ways to login users with Django's authentication tools, and the features that make the authorization controls work. In that topic, middleware came up as an integral component. Now we're going to learn more about middleware and its function within a Django project.
{{< /web >}}
{{< book >}}
-In this chapter,
-we're going to learn more
-about middleware
-and its function
-within a Django project.
+In this chapter, we're going to learn more about middleware and its function within a Django project.
{{< /book >}}
-{{< understand-django-series "middleware" >}}
+{{< understand-django-series-pt "middleware" >}}
## How Should I Think About Middleware?
-To start this topic,
-let's figure out
-where middleware exists
-in a Django project.
-
-Middleware is code that exists
-in the middle.
-"In the middle of what?"
-you might ask.
-The "middle" is the code that executes between
-when an `HttpRequest`
-is created
-by the framework
-and when the code you wrote is called
-by Django.
-The "middle" can also refer
-to code that executes
-*after* your view completes
-but before Django translates the `HttpResponse`
-to bytes
-to send it over the network
-to a browser.
-
-Have you ever eaten an Everlasting Gobstopper?
-No,
-I don't mean the one
-from Willy Wonka
-that will last forever.
-An Everlasting Gobstopper is a hard, layered candy
-that changes colors and flavors
-as you keep it in your mouth
-until you finally get to a soft center.
-
-Middleware is kind of like those candy layers
-and your view code is like the soft center.
-My analogy breaks down when you think
-about how someone eats the candy.
-
-With the candy,
-you experience one layer at a time
-until you get to the middle
-and you're done.
-A more apt comparison to middleware would be
-to burrow *through* the layers
-and come out the other side,
-experiencing the same layers
-in the opposite order
-as the way you came in.
-
-What's shown below is a diagram
-of all the default middleware
-that is included
-when you run the `startproject` command.
-If you're a visual learner
-who didn't find my gobstopper analogy helpful,
-then I hope this picture will be more illustrative.
+To start this topic, let's figure out where middleware exists in a Django project.
+
+Middleware is code that exists in the middle. "In the middle of what?" you might ask. The "middle" is the code that executes between when an `HttpRequest` is created by the framework and when the code you wrote is called by Django. The "middle" can also refer to code that executes *after* your view completes but before Django translates the `HttpResponse` to bytes to send it over the network to a browser.
+
+Have you ever eaten an Everlasting Gobstopper? No, I don't mean the one from Willy Wonka that will last forever. An Everlasting Gobstopper is a hard, layered candy that changes colors and flavors as you keep it in your mouth until you finally get to a soft center.
+
+Middleware is kind of like those candy layers and your view code is like the soft center. My analogy breaks down when you think about how someone eats the candy.
+
+With the candy, you experience one layer at a time until you get to the middle and you're done. A more apt comparison to middleware would be to burrow *through* the layers and come out the other side, experiencing the same layers in the opposite order as the way you came in.
+
+What's shown below is a diagram of all the default middleware that is included when you run the `startproject` command. If you're a visual learner who didn't find my gobstopper analogy helpful, then I hope this picture will be more illustrative:
{{< web >}}
```text
@@ -138,223 +68,87 @@ request ========> view function=======> response
```
{{< /book >}}
-How does Django make this layering work?
-When you start Django
-with an application server
-like Gunicorn,
-you have to give the application server the path
-to your WSGI module.
-We will cover application servers in a later topic,
-but, for now, know that an application server can run your Django app.
-If your project directory containing your settings file
-is called `project`,
-then calling Gunicorn looks like:
+How does Django make this layering work? When you start Django with an application server like Gunicorn, you have to give the application server the path to your WSGI module. We will cover application servers in a later topic, but, for now, know that an application server can run your Django app. If your project directory containing your settings file is called `project`, then calling Gunicorn looks like:
```bash
$ gunicorn project.wsgi
```
-You'd have this setup if you ran `django-admin startproject project .`
-(including the last dot),
-but what's really needed by the application server is wherever your `wsgi.py`
-file is located in your project,
-*in module path form*.
-Adjust accordingly for your needs.
+You'd have this setup if you ran `django-admin startproject project .` (including the last dot), but what's really needed by the application server is wherever your `wsgi.py` file is located in your project, *in module path form*. Adjust accordingly for your needs.
Remember way back
{{< web >}}
-in the first article
-of the series
+in the first article of the series
{{< /web >}}
{{< book >}}
in the first chapter
{{< /book >}}
-that WSGI stands
-for the Web Server Gateway Interface
-and is the common layer
-that synchronous Python web apps must implement
-in order to work
-with Python application servers.
-Inside this `project.wsgi` module
-is a function called `get_wsgi_application`,
-imported from `django.core.wsgi`.
+that WSGI stands for the Web Server Gateway Interface and is the common layer that synchronous Python web apps must implement in order to work with Python application servers. Inside this `project.wsgi` module is a function called `get_wsgi_application`, imported from `django.core.wsgi`.
`get_wsgi_application` does two things:
{{< web >}}
-* Calls `django.setup` which does all the startup configuration
- that we saw in the last article
+* Calls `django.setup` which does all the startup configuration that we saw in the last article
* Returns a `WSGIHandler` instance
{{< /web >}}
{{< book >}}
-* Calls `django.setup` which does all the startup configuration
- that we saw in the last chapter
+* Calls `django.setup` which does all the startup configuration that we saw in the last chapter
* Returns a `WSGIHandler` instance
{{< /book >}}
-As you might guess,
-the `WSGIHandler` is designed
-to make the WSGI interface work,
-but it is also a subclass
-of `django.core.handlers.base.BaseHandler`.
-This base handler class is where Django handles middleware setup.
-
-The base handler includes a `load_middleware` method.
-This method has the job
-of iterating through all the middleware listed
-in your `MIDDLEWARE` setting.
-As it iterates through the `MIDDLEWARE`,
-the method's primary objective is
-to include each middleware
-in the *middleware chain*.
+As you might guess, the `WSGIHandler` is designed to make the WSGI interface work, but it is also a subclass of `django.core.handlers.base.BaseHandler`. This base handler class is where Django handles middleware setup.
+
+The base handler includes a `load_middleware` method. This method has the job of iterating through all the middleware listed in your `MIDDLEWARE` setting. As it iterates through the `MIDDLEWARE`, the method's primary objective is to include each middleware in the *middleware chain*.
> The middleware chain is the Django gobstopper.
-The chain represents each instance of Django middleware,
-layered together,
-to produce the desired effect
-of allowing a request and response
-to pass through each middleware.
+The chain represents each instance of Django middleware, layered together, to produce the desired effect of allowing a request and response to pass through each middleware.
-Aside from building the middleware chain,
-`load_middleware` must do some other important configuration.
+Aside from building the middleware chain, `load_middleware` must do some other important configuration.
{{< web >}}
-* The method handles synchronous and asynchronous middleware.
- As Django increases its support
- of async development,
- the internals of Django need to manage the differences.
- `load_middleware` makes some alterations
- depending on what it can discover about a middleware class.
-* The method registers a middleware with certain *sets*
- of middleware
- based on the presence
- of various hook methods.
- We'll discuss those hooks later in this article.
+* The method handles synchronous and asynchronous middleware. As Django increases its support of async development, the internals of Django need to manage the differences. `load_middleware` makes some alterations depending on what it can discover about a middleware class.
+* The method registers a middleware with certain *sets* of middleware based on the presence of various hook methods. We'll discuss those hooks later in this article.
{{< /web >}}
{{< book >}}
-* The method handles synchronous and asynchronous middleware.
- As Django increases its support
- of async development,
- the internals of Django need to manage the differences.
- `load_middleware` makes some alterations
- depending on what it can discover about a middleware class.
-* The method registers a middleware with certain *sets*
- of middleware
- based on the presence
- of various hook methods.
- We'll discuss those hooks later in this chapter.
+* The method handles synchronous and asynchronous middleware. As Django increases its support of async development, the internals of Django need to manage the differences. `load_middleware` makes some alterations depending on what it can discover about a middleware class.
+* The method registers a middleware with certain *sets* of middleware based on the presence of various hook methods. We'll discuss those hooks later in this chapter.
{{< /book >}}
-That explains middleware's structure
-and how all the middleware interacts
-with the request and response lifecycle,
-but what does middleware do?
-
-We can use middleware
-for a wide variety
-of purposes.
-Because of the middleware chain,
-a successful HTTP request will pass
-through every middleware.
-This property of middleware makes it ideal
-for code that we want to execute globally
-for our Django project.
+That explains middleware's structure and how all the middleware interacts with the request and response lifecycle, but what does middleware do?
+
+We can use middleware for a wide variety of purposes. Because of the middleware chain, a successful HTTP request will pass through every middleware. This property of middleware makes it ideal for code that we want to execute globally for our Django project.
For instance,
{{< web >}}
-think back to our last article
-on
-[User Authentication]({{< ref "/understand-django/2020-11-04-user-authentication.md" >}}).
+think back to our last article on [User Authentication]({{< ref "/understand-django/2020-11-04-user-authentication.pt.md" >}}).
In that article,
{{< /web >}}
{{< book >}}
-think back to our last chapter.
-In that chapter,
+think back to our last chapter. In that chapter,
{{< /book >}}
-we observed
-that Django's auth system is dependent
-on the `AuthenticationMiddleware`.
-This middleware has the singular job
-of adding a `user` property
-to every `HttpRequest` object
-that passes through the application
-before the request gets
-to view code.
-
-The `AuthenticationMiddleware` highlights some qualities
-that are good for middleware
-in Django.
+we observed that Django's auth system is dependent on the `AuthenticationMiddleware`. This middleware has the singular job of adding a `user` property to every `HttpRequest` object that passes through the application before the request gets to view code.
+
+The `AuthenticationMiddleware` highlights some qualities that are good for middleware in Django.
* A middleware should ideally have a narrow or singular objective.
* A middleware should run a minimal amount of code.
-*Why?*
-Again,
-the answer is related
-to the middleware chain.
-Since the HTTP request will pass through every middleware
-in the chain,
-then we can see
-that *every middleware will execute
-for every request.*
-In other words,
-each middleware carries a performance overhead
-for each request.
-
-There **is** an exception to this behavior
-of the chain.
-A middleware early
-in the chain
-can prevent middleware later
-in the chain
-from running.
-
-For example,
-the `SecurityMiddleware` is first
-in the default middleware chain
-from a `startproject` generated project.
-This middleware is designed to do some checks
-to keep the application secure.
-One check is to look for a secure connection
-(i.e., a request using HTTPS)
-if HTTPS is configured.
-If a request comes to the application
-and uses HTTP
-instead of HTTPS,
-the middleware can return an `HttpResponsePermanentRedirect`
-that redirects to the same URL
-with `https://`
-and prevents the rest of the chain
-from running.
-
-Aside from this exceptional behavior
-in middleware,
-it's important
-to remember that,
-in most circumstances,
-each middleware will run
-for each request.
-We should be aware
-of that performance aspect
-when creating our own middleware.
-
-Now we're ready to learn
-about how we can create our own middleware!
+*Why?* Again, the answer is related to the middleware chain. Since the HTTP request will pass through every middleware in the chain, then we can see that *every middleware will execute for every request.* In other words, each middleware carries a performance overhead for each request.
+
+There **is** an exception to this behavior of the chain. A middleware early in the chain can prevent middleware later in the chain from running.
+
+For example, the `SecurityMiddleware` is first in the default middleware chain from a `startproject` generated project. This middleware is designed to do some checks to keep the application secure. One check is to look for a secure connection (i.e., a request using HTTPS) if HTTPS is configured. If a request comes to the application and uses HTTP instead of HTTPS, the middleware can return an `HttpResponsePermanentRedirect` that redirects to the same URL with `https://` and prevents the rest of the chain from running.
+
+Aside from this exceptional behavior in middleware, it's important to remember that, in most circumstances, each middleware will run for each request. We should be aware of that performance aspect when creating our own middleware.
+
+Now we're ready to learn about how we can create our own middleware!
## How Can I Write My Own Custom Middleware?
-Let's assume that you've found a good case
-to create a middleware.
-You need something that happens
-with every request
-and that functionality has a narrow goal.
+Let's assume that you've found a good case to create a middleware. You need something that happens with every request and that functionality has a narrow goal.
-You can begin
-with an empty middleware definition.
-In my example,
-we're going to put the middleware
-in a `middleware.py` file.
+You can begin with an empty middleware definition. In my example, we're going to put the middleware in a `middleware.py` file:
```python
# project/middleware.py
@@ -368,8 +162,7 @@ class AwesomeMiddleware:
)
```
-After creating the middleware,
-you add it to your settings.
+After creating the middleware, you add it to your settings:
```python
# project/settings.py
@@ -381,53 +174,16 @@ MIDDLEWARE = [
]
```
-*That's it!*
-This custom middleware doesn't do anything
-except slow performance slightly
-because it's an extra method call
-on every request.
-Since I put the middleware at the end
-of the `MIDDLEWARE` list,
-it will be the last middleware
-to run before a view receives a request
-and the first middleware
-with the chance
-to process a response.
-
-We can break down how this class works.
-
-* The `__init__` method gets a callable
- that is conventionally named `get_response`.
- The middleware is created during `load_middleware`
- and the callable is a key part
- of what makes the middleware chain work.
- The callable will either call the next middleware
- or the view
- depending on where the current middleware is
- in the chain.
-* The `__call__` method transforms the middleware instance itself
- into a callable.
- The method must call `get_response`
- to ensure that the chain is unbroken.
-
-If you want to do extra work,
-you can make changes
-to the `__call__` method.
-You can modify `__call__`
-to process changes
-before or after the call
-of `get_response`.
-In the request/response lifecycle,
-changes before `get_response`
-occur before the view is called
-while changes after `get_response`
-can handle the `response` itself
-or any other post-request processing.
-
-Let's say we want our example middleware
-to record some timing information.
-We might update the code
-to look like:
+*That's it!* This custom middleware doesn't do anything except slow performance slightly because it's an extra method call on every request. Since I put the middleware at the end of the `MIDDLEWARE` list, it will be the last middleware to run before a view receives a request and the first middleware with the chance to process a response.
+
+We can break down how this class works:
+
+* The `__init__` method gets a callable that is conventionally named `get_response`. The middleware is created during `load_middleware` and the callable is a key part of what makes the middleware chain work. The callable will either call the next middleware or the view depending on where the current middleware is in the chain.
+* The `__call__` method transforms the middleware instance itself into a callable. The method must call `get_response` to ensure that the chain is unbroken.
+
+If you want to do extra work, you can make changes to the `__call__` method. You can modify `__call__` to process changes before or after the call of `get_response`. In the request/response lifecycle, changes before `get_response` occur before the view is called while changes after `get_response` can handle the `response` itself or any other post-request processing.
+
+Let's say we want our example middleware to record some timing information. We might update the code to look like:
```python
# project/middleware.py
@@ -458,49 +214,19 @@ class AwesomeMiddleware:
return response
```
-We still haven't covered logging yet,
-but you can understand it
-as recording messages
-to some output source
-like a file.
-
-This example acts as a crude performance monitor.
-If you wanted to measure the response time
-of a view,
-this middleware would do that.
-The downside is that it wouldn't tell you *which* view is recorded.
-Hey, give me a break,
-this is a silly example! 🤪
-
-Hopefully,
-you're beginning
-to see how middleware can be useful.
-But wait!
-There's more that middleware can do.
-
-A Django middleware can define any
-of three different hook methods
-that Django will run
-at different parts of the request/response lifecycle.
-The three methods are:
-
-* `process_exception` - This hook is called
- whenever a view raises an exception.
- This could include an uncaught exception
- from the view,
- but the hook will also receive exceptions
- that are intentionally raised
- like `Http404`.
-* `process_template_response` - This hook is called
- whenever a view returns a response
- that looks like a template response
- (i.e., the response object has a `render` method).
-* `process_view` - This hook is called
- right before the view.
-
-Returning to our silly example,
-we can make it less silly
-by using the `process_view` hook.
+We still haven't covered logging yet, but you can understand it as recording messages to some output source like a file.
+
+This example acts as a crude performance monitor. If you wanted to measure the response time of a view, this middleware would do that. The downside is that it wouldn't tell you *which* view is recorded. Hey, give me a break, this is a silly example! 🤪
+
+Hopefully, you're beginning to see how middleware can be useful. But wait! There's more that middleware can do.
+
+A Django middleware can define any of three different hook methods that Django will run at different parts of the request/response lifecycle. The three methods are:
+
+* `process_exception` - This hook is called whenever a view raises an exception. This could include an uncaught exception from the view, but the hook will also receive exceptions that are intentionally raised like `Http404`.
+* `process_template_response` - This hook is called whenever a view returns a response that looks like a template response (i.e., the response object has a `render` method).
+* `process_view` - This hook is called right before the view.
+
+Returning to our silly example, we can make it less silly by using the `process_view` hook.
Let's see what we can do:
```python
@@ -537,11 +263,7 @@ class AwesomeMiddleware:
)
```
-Now our middleware uses Python's reflection capabilities
-to record the view's name.
-If accessing the Django admin
-with an unauthenticated user,
-the log might record something like:
+Now our middleware uses Python's reflection capabilities to record the view's name. If accessing the Django admin with an unauthenticated user, the log might record something like:
```text
Tracking 1607438038.232886
@@ -549,186 +271,41 @@ Running login view
Tracking 1607438038.261855 for a delta of 0.02896881103515625
```
-This middleware could still benefit
-from a lot of polish,
-but you can see how the hooks make it possible
-for a middleware to have more advanced functionality.
-
-As an example of the `process_exception` middleware,
-consider a service
-that collects and reports exceptions
-to track the health
-of your application.
-There are many of these services
-like
-{{< extlink "https://rollbar.com/" "Rollbar" >}}
-and
-{{< extlink "https://sentry.io/welcome/" "Sentry" >}}.
-I happen to be a Rollbar user
-so I'll comment
-on that one.
-You can see from the
-{{< extlink "https://github.com/rollbar/pyrollbar/blob/8d116a374f2c54da886972f7da7c289e317bbd8a/rollbar/contrib/django/middleware.py#L268" "pyrollbar code" >}}
-that the service sends exception information
-from the `process_exception` hook
-to Rollbar
-via their `rollbar.report_exc_info` function.
-Without middleware,
-capturing and reporting exceptions would be *significantly* harder.
-
-Want to learn more about hooks?
-You can see all the details
-about these hooks
-in the {{< extlink "https://docs.djangoproject.com/en/4.1/topics/http/middleware/#other-middleware-hooks" "middleware documentation" >}}.
+This middleware could still benefit from a lot of polish, but you can see how the hooks make it possible for a middleware to have more advanced functionality.
+
+As an example of the `process_exception` middleware, consider a service that collects and reports exceptions to track the health of your application. There are many of these services like {{< extlink "https://rollbar.com/" "Rollbar" >}} and {{< extlink "https://sentry.io/welcome/" "Sentry" >}}. I happen to be a Rollbar user so I'll comment on that one. You can see from the {{< extlink "https://github.com/rollbar/pyrollbar/blob/8d116a374f2c54da886972f7da7c289e317bbd8a/rollbar/contrib/django/middleware.py#L268" "pyrollbar code" >}} that the service sends exception information from the `process_exception` hook to Rollbar via their `rollbar.report_exc_info` function. Without middleware, capturing and reporting exceptions would be *significantly* harder.
+
+Want to learn more about hooks? You can see all the details about these hooks in the {{< extlink "https://docs.djangoproject.com/en/4.1/topics/http/middleware/#other-middleware-hooks" "middleware documentation" >}}.
## What Middleware Does Django Include?
-We've looked at the mental model
-for middleware
-and all the details
-of how an individual middleware works.
-What middleware does Django include
-in the framework?
-
-The full list
-of built-in middleware
-is available
-in the {{< extlink "https://docs.djangoproject.com/en/4.1/ref/middleware/" "middleware reference" >}}.
-I'll describe what I think are the most common
-or useful middleware classes
-that Django includes.
+We've looked at the mental model for middleware and all the details of how an individual middleware works. What middleware does Django include in the framework?
+
+The full list of built-in middleware is available in the {{< extlink "https://docs.djangoproject.com/en/4.1/ref/middleware/" "middleware reference" >}}. I'll describe what I think are the most common
+or useful middleware classes that Django includes.
{{< web >}}
-* `AuthenticationMiddleware` - We've already encountered this middleware
- in the exploration of the auth system.
- The job of this middleware is
- to add the `user` attribute
- to an `HttpRequest` object.
- That one little `user` attribute powers many
- of the features of the auth system.
-* `CommonMiddleware` - The common middleware is a bit of an oddball.
- This middleware handles a variety
- of Django settings
- to control certain aspects
- of your project.
- For instance,
- the `APPEND_SLASH` setting will redirect a request
- like `example.com/accounts`
- to `example.com/accounts/`.
- This setting only works
- if the `CommonMiddleware` is included.
-* `CsrfViewMiddleware` - In the forms article,
- I mentioned the CSRF token.
- As a reminder,
- this is a security feature
- that helps protect your project against malicious sources
- that want to send bad data
- to your site.
- The `CsrfViewMiddleware` ensures
- that the CSRF token is present and valid
- on form submissions.
-* `LocaleMiddleware` - This middleware is for handling translations
- if you choose to internationalize your project.
-* `MessageMiddleware` - The message middleware is for "flash messages."
- These are one-time messages
- that you'd likely see
- after submitting a form,
- though they can be used
- in many places.
- We'll discuss messages more
- when we get to the sessions topic.
-* `SecurityMiddleware` - The security middleware includes a number
- of checks
- to help keep your site secure.
- We saw the example of checking for HTTPS earlier
- in this article.
- This middleware also handles things like XSS, HSTS,
- and a bunch of other acronyms (😛)
- that will be seen
- with the future security topic.
-* `SessionMiddleware` - The session middleware manages
- session state
- for a user.
- Sessions are crucial
- for many parts of Django
- like user auth.
+* `AuthenticationMiddleware` - We've already encountered this middleware in the exploration of the auth system. The job of this middleware is to add the `user` attribute to an `HttpRequest` object. That one little `user` attribute powers many of the features of the auth system.
+* `CommonMiddleware` - The common middleware is a bit of an oddball. This middleware handles a variety of Django settings to control certain aspects of your project. For instance, the `APPEND_SLASH` setting will redirect a request like `example.com/accounts` to `example.com/accounts/`. This setting only works if the `CommonMiddleware` is included.
+* `CsrfViewMiddleware` - In the forms article, I mentioned the CSRF token. As a reminder, this is a security feature that helps protect your project against malicious sources that want to send bad data to your site. The `CsrfViewMiddleware` ensures that the CSRF token is present and valid on form submissions.
+* `LocaleMiddleware` - This middleware is for handling translations if you choose to internationalize your project.
+* `MessageMiddleware` - The message middleware is for "flash messages." These are one-time messages that you'd likely see after submitting a form, though they can be used in many places. We'll discuss messages more when we get to the sessions topic.
+* `SecurityMiddleware` - The security middleware includes a number of checks to help keep your site secure. We saw the example of checking for HTTPS earlier in this article. This middleware also handles things like XSS, HSTS, and a bunch of other acronyms (😛) that will be seen with the future security topic.
+* `SessionMiddleware` - The session middleware manages session state for a user. Sessions are crucial for many parts of Django like user auth.
{{< /web >}}
{{< book >}}
-* `AuthenticationMiddleware` - We've already encountered this middleware
- in the exploration of the auth system.
- The job of this middleware is
- to add the `user` attribute
- to an `HttpRequest` object.
- That one little `user` attribute powers many
- of the features of the auth system.
-* `CommonMiddleware` - The common middleware is a bit of an oddball.
- This middleware handles a variety
- of Django settings
- to control certain aspects
- of your project.
- For instance,
- the `APPEND_SLASH` setting will redirect a request
- like `example.com/accounts`
- to `example.com/accounts/`.
- This setting only works
- if the `CommonMiddleware` is included.
-* `CsrfViewMiddleware` - In the forms chapter,
- I mentioned the CSRF token.
- As a reminder,
- this is a security feature
- that helps protect your project against malicious sources
- that want to send bad data
- to your site.
- The `CsrfViewMiddleware` ensures
- that the CSRF token is present and valid
- on form submissions.
-* `LocaleMiddleware` - This middleware is for handling translations
- if you choose to internationalize your project.
-* `MessageMiddleware` - The message middleware is for "flash messages."
- These are one-time messages
- that you'd likely see
- after submitting a form,
- though they can be used
- in many places.
- We'll discuss messages more
- when we get to the sessions topic.
-* `SecurityMiddleware` - The security middleware includes a number
- of checks
- to help keep your site secure.
- We saw the example of checking for HTTPS earlier
- in this chapter.
- This middleware also handles things like XSS, HSTS,
- and a bunch of other acronyms (😛)
- that will be seen
- with the future security topic.
-* `SessionMiddleware` - The session middleware manages
- session state
- for a user.
- Sessions are crucial
- for many parts of Django
- like user auth.
+* `AuthenticationMiddleware` - We've already encountered this middleware in the exploration of the auth system. The job of this middleware is to add the `user` attribute to an `HttpRequest` object. That one little `user` attribute powers many of the features of the auth system.
+* `CommonMiddleware` - The common middleware is a bit of an oddball. This middleware handles a variety of Django settings to control certain aspects of your project. For instance, the `APPEND_SLASH` setting will redirect a request like `example.com/accounts` to `example.com/accounts/`. This setting only works if the `CommonMiddleware` is included.
+* `CsrfViewMiddleware` - In the forms chapter, I mentioned the CSRF token. As a reminder, this is a security feature that helps protect your project against malicious sources that want to send bad data to your site. The `CsrfViewMiddleware` ensures that the CSRF token is present and valid on form submissions.
+* `LocaleMiddleware` - This middleware is for handling translations if you choose to internationalize your project.
+* `MessageMiddleware` - The message middleware is for "flash messages." These are one-time messages that you'd likely see after submitting a form, though they can be used in many places. We'll discuss messages more when we get to the sessions topic.
+* `SecurityMiddleware` - The security middleware includes a number of checks to help keep your site secure. We saw the example of checking for HTTPS earlier in this chapter. This middleware also handles things like XSS, HSTS, and a bunch of other acronyms (😛) that will be seen with the future security topic.
+* `SessionMiddleware` - The session middleware manages session state for a user. Sessions are crucial for many parts of Django like user auth.
{{< /book >}}
-As you can see
-from this incomplete list,
-Django's middleware can do a lot
-to enrich your project
-in a wide variety
-of ways.
-Middleware is an extremely powerful concept
-for Django projects
-and a great tool
-to extend your application's request handling.
-
-Remember,
-middleware comes with a performance cost
-so avoid the temptation
-to stuff too much functionality
-into the middleware chain.
-As long as you're aware
-of the tradeoffs,
-middleware is a great tool
-for your toolbelt.
+As you can see from this incomplete list, Django's middleware can do a lot to enrich your project in a wide variety of ways. Middleware is an extremely powerful concept for Django projects and a great tool to extend your application's request handling.
+
+Remember, middleware comes with a performance cost so avoid the temptation to stuff too much functionality into the middleware chain. As long as you're aware of the tradeoffs, middleware is a great tool for your toolbelt.
## Summary
@@ -752,12 +329,8 @@ Next time we'll dig into static files.
{{< book >}}
In the next chapter, we'll dig into static files.
{{< /book >}}
-Static files are all the images,
-JavaScript,
-CSS,
-or other file types
-that your application serves, unmodified,
-to a user.
+Static files are all the images, JavaScript, CSS, or other file types that your application serves, unmodified, to a user.
+
We need to understand:
* How to configure static files
@@ -767,15 +340,6 @@ We need to understand:
to the internet
{{< web >}}
-If you'd like to follow along
-with the series,
-please feel free to sign up
-for my newsletter
-where I announce all of my new content.
-If you have other questions,
-you can reach me online
-on Twitter
-where I am
-{{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
+If you'd like to follow along with the series, please feel free to sign up for my newsletter where I announce all of my new content. If you have other questions, you can reach me online on Twitter where I am {{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
{{< /web >}}
From 7ef4adb29c2392ce0013fcce9de233a4734bc333 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nazar=C3=A9=20da=20Piedade?=
Date: Fri, 5 May 2023 11:17:46 +0100
Subject: [PATCH 12/30] chore: format
`understand-django/2021-01-06-serving-static-files.pt.md`
---
.../2021-01-06-serving-static-files.pt.md | 702 +++---------------
1 file changed, 111 insertions(+), 591 deletions(-)
diff --git a/content/understand-django/2021-01-06-serving-static-files.pt.md b/content/understand-django/2021-01-06-serving-static-files.pt.md
index 550e1f14..984b31c1 100644
--- a/content/understand-django/2021-01-06-serving-static-files.pt.md
+++ b/content/understand-django/2021-01-06-serving-static-files.pt.md
@@ -1,12 +1,7 @@
---
title: "Serving Static Files"
description: >-
- In this Understand Django article,
- we'll examine static files.
- Static files are critical to apps,
- but have little to do with Python code.
- We'll see what they are
- and what they do.
+ In this Understand Django article, we'll examine static files. Static files are critical to apps, but have little to do with Python code. We'll see what they are and what they do.
image: img/django.png
type: post
categories:
@@ -22,109 +17,41 @@ series: "Understand Django"
In the previous
{{< web >}}
-[Understand Django]({{< ref "/understand-django/_index.md" >}})
-article,
+[Understand Django]({{< ref "/understand-django/_index.pt.md" >}}) article,
{{< /web >}}
{{< book >}}
chapter,
{{< /book >}}
-I described how Django gives us tools
-to run code
-for any request
-using the middleware system.
-Our next focus will be on static files.
-Static files are vital
-to your application,
-but they have little to do with Python code.
-We'll see what they are
-and what they do.
-
-{{< understand-django-series "static" >}}
+I described how Django gives us tools to run code for any request using the middleware system. Our next focus will be on static files. Static files are vital to your application, but they have little to do with Python code. We'll see what they are and what they do.
+
+{{< understand-django-series-pt "static" >}}
## What Are Static Files?
-Static files are files
-that don't change
-when your application is running.
+Static files are files that don't change when your application is running.
-These files do a lot to improve your application,
-but they aren't dynamically generated
-by your Python web server
-like a usual HTML response.
-In a typical web application,
-your most common static files will be the following types:
+These files do a lot to improve your application, but they aren't dynamically generated by your Python web server like a usual HTML response. In a typical web application, your most common static files will be the following types:
* Cascading Style Sheets, CSS
* JavaScript
* Images
-Keep in mind that even though Django will serve these files statically,
-there may be a complex process
-in place
-to produce the files.
-For instance,
-modern JavaScript apps often
-use complex build tools
-like {{< extlink "https://webpack.js.org/" "webpack" >}}
-to build the final JavaScript files
-that are served
-to users.
-
-Static files are crucial
-to your Django project
-because the modern web requires more than dynamically generated HTML markup.
-Do you visit any website
-that has *zero* styling
-of its HTML?
-These kinds of sites exist
-and can be awesome
-for making a quick tool,
-but most users expect websites
-to be aesthetically pleasing.
-For us,
-that means that we should be prepared
-to include some CSS styling
-at a minimum.
-
-Let's look at some configuration
-to see where static files live in your project,
-then begin to work with some examples.
+Keep in mind that even though Django will serve these files statically, there may be a complex process in place to produce the files. For instance, modern JavaScript apps often use complex build tools like {{< extlink "https://webpack.js.org/" "webpack" >}}
+to build the final JavaScript files that are served to users.
+
+Static files are crucial to your Django project because the modern web requires more than dynamically generated HTML markup. Do you visit any website that has *zero* styling of its HTML? These kinds of sites exist and can be awesome for making a quick tool, but most users expect websites to be aesthetically pleasing. For us, that means that we should be prepared to include some CSS styling at a minimum.
+
+Let's look at some configuration to see where static files live in your project, then begin to work with some examples.
## Configuration
-To use static files
-in your project,
-you need the `django.contrib.staticfiles` app
-in your project's `INSTALLED_APPS` list.
-This is another one
-of the default Django applications
-that Django will include
-if you start from the `startproject` command.
-
-The `staticfiles` app has a handful
-of {{< extlink "https://docs.djangoproject.com/en/4.1/ref/settings/#settings-staticfiles" "settings" >}}
-that we need to consider to start.
-
-I'm going to make the same recommendation
-about static files
-as I did with templates.
-I recommend that you create a `static` directory
-at the root
-of your project
-to hold your static files.
-Similarly to templates,
-the `staticfiles` app will look for `static` directories
-within each of your Django apps
-to find files,
-but I find it easier to work with and locate static files
-if they are all
-in the same directory.
-
-To make that setup work,
-use the `STATICFILES_DIRS` setting.
-This setting tells Django any additional locations
-for static files beyond looking for a `static` directory
-within each app.
+To use static files in your project, you need the `django.contrib.staticfiles` app in your project's `INSTALLED_APPS` list. This is another one of the default Django applications that Django will include if you start from the `startproject` command.
+
+The `staticfiles` app has a handful of {{< extlink "https://docs.djangoproject.com/en/4.1/ref/settings/#settings-staticfiles" "settings" >}} that we need to consider to start.
+
+I'm going to make the same recommendation about static files as I did with templates. I recommend that you create a `static` directory at the root of your project to hold your static files. Similarly to templates, the `staticfiles` app will look for `static` directories within each of your Django apps to find files, but I find it easier to work with and locate static files if they are all in the same directory.
+
+To make that setup work, use the `STATICFILES_DIRS` setting. This setting tells Django any additional locations for static files beyond looking for a `static` directory within each app:
```python
# project/settings.py
@@ -134,21 +61,7 @@ within each app.
STATICFILES_DIRS = [BASE_DIR / "static"]
```
-Next,
-we can define the URL path prefix
-that Django will use
-when it serves a static file.
-Let's say you have `site.css`
-in the root
-of your project's `static` directory.
-You probably wouldn't want the file
-to be accessible as `mysite.com/site.css`.
-To do so would mean that static files could conflict
-with URL paths
-that your app might need to direct
-to a view.
-The `STATIC_URL` setting lets us namespace our static files
-and, as the {{< extlink "https://www.python.org/dev/peps/pep-0020/" "Zen of Python" >}} says:
+Next, we can define the URL path prefix that Django will use when it serves a static file. Let's say you have `site.css` in the root of your project's `static` directory. You probably wouldn't want the file to be accessible as `mysite.com/site.css`. To do so would mean that static files could conflict with URL paths that your app might need to direct to a view. The `STATIC_URL` setting lets us namespace our static files and, as the {{< extlink "https://www.python.org/dev/peps/pep-0020/" "Zen of Python" >}} says:
> Namespaces are one honking great idea -- let's do more of those!
@@ -161,32 +74,11 @@ STATICFILES_DIRS = [BASE_DIR / "static"]
STATIC_URL = '/static/'
```
-With `STATIC_URL` set,
-we can access `site.css`
-from `mysite.com/static/site.css`.
-
-There's one more crucial setting
-that we need to set,
-and it is called `STATIC_ROOT`.
-When we deploy our Django project,
-Django wants to find all static files
-from a single directory.
-The reason for this is for efficiency.
-It's possible for Django
-to search through all the app `static` directories
-and any directories set
-in `STATICFILES_DIRS`
-whenever it searches for a file
-to serve,
-but that would be slow.
-
-Instead,
-Django will put all static files
-into a single directory
-so that searching for a file is a search
-through a single file tree.
-We'll look more at how this happens
-in the deployment section later
+With `STATIC_URL` set, we can access `site.css` from `mysite.com/static/site.css`.
+
+There's one more crucial setting that we need to set, and it is called `STATIC_ROOT`. When we deploy our Django project, Django wants to find all static files from a single directory. The reason for this is for efficiency. It's possible for Django to search through all the app `static` directories and any directories set in `STATICFILES_DIRS` whenever it searches for a file to serve, but that would be slow.
+
+Instead, Django will put all static files into a single directory so that searching for a file is a search through a single file tree. We'll look more at how this happens in the deployment section later
{{< web >}}
in this article.
{{< /web >}}
@@ -194,21 +86,7 @@ in this article.
in this chapter.
{{< /book >}}
-Once we set `STATIC_ROOT`,
-Django will have the desired output location
-for static files.
-If you set the path somewhere
-in your repository,
-don't forget to put that path
-in your `.gitignore`
-if you're using version control
-with {{< extlink "https://git-scm.com/" "Git" >}}
-(and I highly recommend that you do!).
-Without that addition to `.gitignore`,
-you'll needlessly add the generated files
-to version control.
-I happen to set my `STATIC_ROOT`
-to a `staticfiles` directory.
+Once we set `STATIC_ROOT`, Django will have the desired output location for static files. If you set the path somewhere in your repository, don't forget to put that path in your `.gitignore` if you're using version control with {{< extlink "https://git-scm.com/" "Git" >}} (and I highly recommend that you do!). Without that addition to `.gitignore`, you'll needlessly add the generated files to version control. I happen to set my `STATIC_ROOT` to a `staticfiles` directory:
```python
# project/settings.py
@@ -220,17 +98,11 @@ STATIC_ROOT = BASE_DIR / "staticfiles"
STATIC_URL = '/static/'
```
-Now that we know how to configure static files,
-we're ready to see how to use them
-in our Django code.
+Now that we know how to configure static files, we're ready to see how to use them in our Django code.
## Working With Static Files
-The primary way
-of working with static files
-is with a template tag.
-The `static` template tag will help render the proper URL
-for a static file for your site.
+The primary way of working with static files is with a template tag. The `static` template tag will help render the proper URL for a static file for your site.
Here's an example template to consider:
@@ -263,74 +135,20 @@ Here's an example template to consider:
```
{{< /book >}}
-In this example,
-I'm assuming that there is a `css` directory
-in my `static` directory
-with a `site.css` file inside.
-Django will render this template tag
-as `/static/css/site.css`
-in the most basic format.
-We should also note
-that I had to include `{% load static %}`
-to ensure that the `static` template tag was available.
-
-In practice,
-I find that this `load` requirement bites me all the time.
-Thankfully, the `TemplateSyntaxError`
-that Django will raise provides a good clue
-on how to fix this issue.
-The exception says "Did you forget to register or load this tag?"
-How helpful of the Django developers
-to tell us what we're probably missing!
-
-Since we know that `STATIC_URL` is `/static/`
-from the configuration section,
-why don't I hardcode the link tag path
-to `/static/css/site.css`?
-You could,
-and that might work,
-but you'll probably run into some long term problems.
-
-* What if you ever wanted to change `STATIC_URL`?
- Maybe you want to change it to something shorter like `/s/`.
- If you hardcode the name,
- now you have more than one place to change.
-* Using some extra features,
- Django may change the name
- of a file to something unique
- by adding a hash to the file name.
- With a hardcoded path of `/static/css/site.css`,
- this may lead to a 404 response
- if Django expects the unique name instead.
- We'll see what the unique name is for
- in the next section.
-
-We should remember
-to use the `static` tag
-in the same way
-that we use the `url` tag
-when we want to resolve a Django URL path.
-Both of these tags help avoid hardcoding paths
-that can change.
-
-Less commonly,
-we can refer to a static file
-from Python code.
-You can do this
-by calling a `static` function defined
-in the same location as the `static` template tag function,
-but the function is not located
-where you might expect it.
-Instead of importing
-from the `staticfiles` app,
-Django defines these functions
-in `django.templatetags.static`.
-
-For example,
-if you wanted to serve a JSON view
-that feeds a JavaScript client application the path
-to a CSS file,
-you might write:
+In this example, I'm assuming that there is a `css` directory in my `static` directory with a `site.css` file inside. Django will render this template tag as `/static/css/site.css` in the most basic format. We should also note that I had to include `{% load static %}` to ensure that the `static` template tag was available.
+
+In practice, I find that this `load` requirement bites me all the time. Thankfully, the `TemplateSyntaxError` that Django will raise provides a good clue on how to fix this issue. The exception says "Did you forget to register or load this tag?" How helpful of the Django developers to tell us what we're probably missing!
+
+Since we know that `STATIC_URL` is `/static/` from the configuration section, why don't I hardcode the link tag path to `/static/css/site.css`? You could, and that might work, but you'll probably run into some long term problems.
+
+* What if you ever wanted to change `STATIC_URL`? Maybe you want to change it to something shorter like `/s/`. If you hardcode the name, now you have more than one place to change.
+* Using some extra features, Django may change the name of a file to something unique by adding a hash to the file name. With a hardcoded path of `/static/css/site.css`, this may lead to a 404 response if Django expects the unique name instead. We'll see what the unique name is for in the next section.
+
+We should remember to use the `static` tag in the same way that we use the `url` tag when we want to resolve a Django URL path. Both of these tags help avoid hardcoding paths that can change.
+
+Less commonly, we can refer to a static file from Python code. You can do this by calling a `static` function defined in the same location as the `static` template tag function, but the function is not located where you might expect it. Instead of importing from the `staticfiles` app, Django defines these functions in `django.templatetags.static`.
+
+For example, if you wanted to serve a JSON view that feeds a JavaScript client application the path to a CSS file, you might write:
```python
# application/views.py
@@ -346,66 +164,24 @@ def get_css(request):
)
```
-In my years of experience
-as a Django developer,
-I've only seen `static` used
-in views a handful
-of times.
-`static` is certainly more widely used
-in templates.
-
-When using static files,
-there are some important considerations
-for deploying your application
-for wider use
-on the internet.
-On its own,
-deployment is a large topic
+In my years of experience as a Django developer, I've only seen `static` used in views a handful of times. `static` is certainly more widely used in templates.
+
+When using static files, there are some important considerations for deploying your application for wider use on the internet. On its own, deployment is a large topic
{{< web >}}
that we'll cover in a future article,
{{< /web >}}
{{< book >}}
that we'll cover in a future chapter,
{{< /book >}}
-but we'll focus
-on static files deployment issues next.
+but we'll focus on static files deployment issues next.
## Deployment Considerations
-In the configuration section,
-we saw the `STATIC_ROOT` option.
-That option will collect all the static files
-into a single directory,
-but *when* does it do that?
-And how do static files work
-when we run in development mode
-and don't have all the files
-in the `STATIC_ROOT` location?
-
-When you deploy your application
-to a server,
-one crucial setting
-to disable
-is the `DEBUG` setting.
-If `DEBUG` is on,
-all kinds of secret data can leak
-from your application,
-so the Django developers *expect* `DEBUG` to be `False`
-for your live site.
-Because of this expectation,
-certain parts of Django behave differently
-when `DEBUG` changes,
-and the `staticfiles` app is one such part.
-
-When `DEBUG` is `True`
-and you are using the `runserver` command
-to run the development web server,
-Django will search for files
-using a set of "finders"
-whenever a user requests a static file.
-These finders are defined
-by the `STATICFILES_FINDERS` setting,
-which defaults to:
+In the configuration section, we saw the `STATIC_ROOT` option. That option will collect all the static files into a single directory, but *when* does it do that? And how do static files work when we run in development mode and don't have all the files in the `STATIC_ROOT` location?
+
+When you deploy your application to a server, one crucial setting to disable is the `DEBUG` setting. If `DEBUG` is on, all kinds of secret data can leak from your application, so the Django developers *expect* `DEBUG` to be `False` for your live site. Because of this expectation, certain parts of Django behave differently when `DEBUG` changes, and the `staticfiles` app is one such part.
+
+When `DEBUG` is `True` and you are using the `runserver` command to run the development web server, Django will search for files using a set of "finders" whenever a user requests a static file. These finders are defined by the `STATICFILES_FINDERS` setting, which defaults to:
```python
[
@@ -414,48 +190,13 @@ which defaults to:
]
```
-As you might guess,
-the `FileSystemFinder` looks for any static files
-found in the file system directory
-that we listed in `STATICFILES_DIRS`.
-The `AppDirectoriesFinder` looks for static files
-in the `static` directory
-of each Django application
-that you have.
-You can see how this gets slow
-when you realize
-that Django will walk
-through `len(STATICFILES_DIRS) + len(INSTALLED_APPS)`
-before giving up
-to find a single file.
-
-To make this whole process faster,
-we turn `DEBUG` to `False`.
-When `DEBUG` is `False`,
-all of the slow machinery
-that searches
-for files throughout your project
-for static file requests is turned off.
-Django only looks
-in the `STATIC_ROOT` directory
-for files.
-
-Since the finders are off
-when `DEBUG` is `True`,
-we have to make sure
-that `STATIC_ROOT` is filled
-with all the proper files.
-To put all the static files
-into place,
-you can use the `collectstatic` command.
-
-`collectstatic` will copy all the files it discovers
-from iterating through each finder
-and collecting files
-from what a finder lists.
-In my example below,
-my Django project directory is `myproject`,
-and I set `STATIC_ROOT` to `staticfiles`.
+As you might guess, the `FileSystemFinder` looks for any static files found in the file system directory that we listed in `STATICFILES_DIRS`. The `AppDirectoriesFinder` looks for static files in the `static` directory of each Django application that you have. You can see how this gets slow when you realize that Django will walk through `len(STATICFILES_DIRS) + len(INSTALLED_APPS)` before giving up to find a single file.
+
+To make this whole process faster, we turn `DEBUG` to `False`. When `DEBUG` is `False`, all of the slow machinery that searches for files throughout your project for static file requests is turned off. Django only looks in the `STATIC_ROOT` directory for files.
+
+Since the finders are off when `DEBUG` is `True`, we have to make sure that `STATIC_ROOT` is filled with all the proper files. To put all the static files into place, you can use the `collectstatic` command.
+
+`collectstatic` will copy all the files it discovers from iterating through each finder and collecting files from what a finder lists. In my example below, my Django project directory is `myproject`, and I set `STATIC_ROOT` to `staticfiles`:
```bash
$ ./manage.py collectstatic
@@ -463,137 +204,36 @@ $ ./manage.py collectstatic
42 static files copied to '/Users/matt/myproject/staticfiles'.
```
-When deploying your application
-to your server,
-you would run `collectstatic`
-before starting the web server.
-By doing that,
-you ensure that the web server
-can access any static files
-that the Django app might request.
+When deploying your application to your server, you would run `collectstatic` before starting the web server. By doing that, you ensure that the web server can access any static files that the Django app might request.
-Can we make this better?
-You bet!
+Can we make this better? You bet!
### Optimizing Performance In Django
-`staticfiles` has another setting worth considering.
-I didn't mention it in the configuration section
-because it's not a critical setting
-to make static files work,
-but we're ready for the setting
-as we're thinking about optimization.
-We should really consider this setting for our projects
-because Django is fairly slow at serving static files
-compared to some of the alternative options that are available
-in the ecosystem.
-
-The last setting we'll consider is the `STATICFILES_STORAGE` setting.
-This setting controls how static files are stored and accessed
-by Django.
-We may want to change `STATICFILES_STORAGE`
-to improve the efficiency
-of the application.
-The biggest boost we can get from this setting
-will provide file caching.
-
-In an ideal world,
-your application would only have to serve a static file exactly *one* time
-to a user's browser.
-In that scenario,
-if an application needed to use the file again,
-then the browser would reuse the *cached* file
-that it already retrieved.
-The challenge that we have is that static files (ironically?)
-change over time.
-
-Say,
-for instance,
-you changed `site.css`
-to change the styling
-of the application.
-You wouldn't want a browser
-to reuse the old version
-because it's missing the latest and greatest changes
-that you made.
-How do we get the benefit
-of telling a browser
-to cache a file for a long time
-to be as efficient as possible
-while still having the flexibility
-to make changes
-and make the user's browser fetch a new version
-of the file?
-
-The "trick" is to serve a "fingerprinted" version
-of the file.
-As a part of the deployment process,
-we would like to uniquely identify each file
-with some kind of version information.
-An easy way for a computer to do this is
-to take the file's content
-and calculate a hash value.
-We can have code take `site.css`,
-calculate the hash,
-and generate a file with the same content,
-but with a different filename
-like `site.abcd1234.css`
-if `abcd1234` was the generated hash value.
-
-The next part of the process is
-to make the template rendering use the `site.abcd1234.css` name.
-Remember how we used the `static` template tag
-instead of hardcoding `/static/css/site.css`?
-This example is a great reason why we did that.
-By using the `static` tag,
-Django can render the filename
-that includes the hash
-instead of only using `site.css`.
-
-The final bit that brings this scheme together is
-to tell the browser
-to cache `site.abcd1234.css`
-for a very long time
-by sending back a certain caching header
-in the HTTP response.
-
-Now,
-we've got the best of both worlds.
-
-* If the user fetches `site.abcd1234.css`,
- their browser will keep it
- for a long time
- and never need to download it again.
- This can be reused every time the user visits a page
- in your app.
-* If we ever change `site.css`,
- then the deployment process can generate a new file
- like `site.ef567890.css`.
- When the user makes a request,
- the HTML will include the new version,
- their browser won't have it in the cache,
- and the browser will download the new version
- with your new changes.
-
-Great!
-How do we get this
-and how much work is it going to require?
-The answer comes back
-to the `STATICFILES_STORAGE` setting
-and a tool called
-{{< extlink "http://whitenoise.evans.io/en/stable/" "WhiteNoise" >}}
-(get it!? "white noise" *is* "static." har har).
-
-WhiteNoise is a pretty awesome piece of software.
-The library will handle
-that *entire* caching scheme
-that I described above.
-
-To set up WhiteNoise,
-you install it with `pip install whitenoise`.
-Then,
-you need to change your `MIDDLEWARE` list
-and `STATICFILES_STORAGE` settings.
+`staticfiles` has another setting worth considering. I didn't mention it in the configuration section because it's not a critical setting to make static files work, but we're ready for the setting as we're thinking about optimization. We should really consider this setting for our projects because Django is fairly slow at serving static files compared to some of the alternative options that are available in the ecosystem.
+
+The last setting we'll consider is the `STATICFILES_STORAGE` setting. This setting controls how static files are stored and accessed by Django. We may want to change `STATICFILES_STORAGE` to improve the efficiency of the application. The biggest boost we can get from this setting will provide file caching.
+
+In an ideal world, your application would only have to serve a static file exactly *one* time to a user's browser. In that scenario, if an application needed to use the file again, then the browser would reuse the *cached* file that it already retrieved. The challenge that we have is that static files (ironically?) change over time.
+
+Say, for instance, you changed `site.css` to change the styling of the application. You wouldn't want a browser to reuse the old version because it's missing the latest and greatest changes that you made. How do we get the benefit of telling a browser to cache a file for a long time to be as efficient as possible while still having the flexibility to make changes and make the user's browser fetch a new version of the file?
+
+The "trick" is to serve a "fingerprinted" version of the file. As a part of the deployment process, we would like to uniquely identify each file with some kind of version information. An easy way for a computer to do this is to take the file's content and calculate a hash value. We can have code take `site.css`, calculate the hash, and generate a file with the same content, but with a different filename like `site.abcd1234.css` if `abcd1234` was the generated hash value.
+
+The next part of the process is to make the template rendering use the `site.abcd1234.css` name. Remember how we used the `static` template tag instead of hardcoding `/static/css/site.css`? This example is a great reason why we did that. By using the `static` tag, Django can render the filename that includes the hash instead of only using `site.css`.
+
+The final bit that brings this scheme together is to tell the browser to cache `site.abcd1234.css` for a very long time by sending back a certain caching header in the HTTP response.
+
+Now, we've got the best of both worlds:
+
+* If the user fetches `site.abcd1234.css`, their browser will keep it for a long time and never need to download it again. This can be reused every time the user visits a page in your app.
+* If we ever change `site.css`, then the deployment process can generate a new file like `site.ef567890.css`. When the user makes a request, the HTML will include the new version, their browser won't have it in the cache, and the browser will download the new version with your new changes.
+
+Great! How do we get this and how much work is it going to require? The answer comes back to the `STATICFILES_STORAGE` setting and a tool called {{< extlink "http://whitenoise.evans.io/en/stable/" "WhiteNoise" >}} (get it!? "white noise" *is* "static." har har).
+
+WhiteNoise is a pretty awesome piece of software. The library will handle that *entire* caching scheme that I described above.
+
+To set up WhiteNoise, you install it with `pip install whitenoise`. Then, you need to change your `MIDDLEWARE` list and `STATICFILES_STORAGE` settings:
```python
# project/settings.py
@@ -610,144 +250,39 @@ STATICFILES_STORAGE = \
'whitenoise.storage.CompressedManifestStaticFilesStorage'
```
-That's about it!
-With this setup,
-WhiteNoise will do a bunch
-of work
-during the `collectstatic` command.
-The library will generate fingerprinted files
-like `site.abcd1234.css`,
-and it will generate compressed versions
-of those files
-using the gzip compression algorithm
-(and, optionally, the
-{{< extlink "https://en.wikipedia.org/wiki/Brotli" "brotli" >}}
-compression algorithm).
-Those extra files look
-like `site.abcd1234.css.gz`
-or `site.abcd1234.css.br`.
-
-When your application runs,
-the WhiteNoise middleware will handle which files
-to serve.
-Because files are static
-and don't require dynamic processing,
-we include the middleware high
-on the list
-to skip a lot of needless extra Python processing.
-In my configuration example,
-I left the `SecurityMiddleware` above WhiteNoise
-so the app can still benefit
-from certain security protections.
-
-As a user's browser makes a request
-for a fingerprinted file,
-the browser can include a request header
-to indicate what compressed formats it can handle.
-Sending compressed files is *way* faster
-than sending uncompressed files over a network.
-WhiteNoise will read the appropriate header
-and try to respond
-with the gzip or brotli version.
-
-The scheme that I described is not the only way
-to handle static files.
-In fact,
-there are some tradeoffs
-to think about:
-
-1. Building with WhiteNoise means
- that we only need to deploy a single app
- and let Python handle all
- of the processing.
-2. Python, for all its benefits,
- is not the fastest programming language out there.
- Leaving Python to serve your static requests will run slower
- than some other methods.
- Additionally,
- your web server's processes must spend time serving the static files
- rather than being fully devoted to dynamic requests.
+That's about it! With this setup, WhiteNoise will do a bunch of work during the `collectstatic` command. The library will generate fingerprinted files like `site.abcd1234.css`, and it will generate compressed versions of those files using the gzip compression algorithm (and, optionally, the {{< extlink "https://en.wikipedia.org/wiki/Brotli" "brotli" >}} compression algorithm). Those extra files look like `site.abcd1234.css.gz` or `site.abcd1234.css.br`.
+
+When your application runs, the WhiteNoise middleware will handle which files to serve. Because files are static and don't require dynamic processing, we include the middleware high on the list to skip a lot of needless extra Python processing. In my configuration example, I left the `SecurityMiddleware` above WhiteNoise so the app can still benefit from certain security protections.
+
+As a user's browser makes a request for a fingerprinted file, the browser can include a request header to indicate what compressed formats it can handle. Sending compressed files is *way* faster than sending uncompressed files over a network. WhiteNoise will read the appropriate header and try to respond with the gzip or brotli version.
+
+The scheme that I described is not the only way to handle static files. In fact, there are some tradeoffs to think about:
+
+1. Building with WhiteNoise means that we only need to deploy a single app and let Python handle all of the processing.
+2. Python, for all its benefits, is not the fastest programming language out there. Leaving Python to serve your static requests will run slower than some other methods. Additionally, your web server's processes must spend time serving the static files rather than being fully devoted to dynamic requests.
### Optimizing Performance With A Reverse Proxy
-An alternative approach
-to using Django
-to serve static files
-is to use another program
-as a *reverse proxy*.
-This setup is more complex,
-but it can offer better performance
-if you need it.
-A reverse proxy is software
-that sits between your users
-and your Django application server.
-CloudFlare has a
-{{< extlink "https://www.cloudflare.com/learning/cdn/glossary/reverse-proxy/" "good article" >}}
-if you want to understand why "reverse" is in the name.
-
-If you set up a reverse proxy,
-you can instruct it
-to handle many things,
-including URL paths
-coming to your site's domain.
-This is where `STATIC_ROOT` and `collectstatic` are useful outside of Django.
-You can set a reverse proxy
-to serve all the files
-that Django collects
-into `STATIC_ROOT`.
+An alternative approach to using Django to serve static files is to use another program as a *reverse proxy*. This setup is more complex, but it can offer better performance if you need it. A reverse proxy is software that sits between your users and your Django application server. CloudFlare has a {{< extlink "https://www.cloudflare.com/learning/cdn/glossary/reverse-proxy/" "good article" >}} if you want to understand why "reverse" is in the name.
+
+If you set up a reverse proxy, you can instruct it to handle many things, including URL paths coming to your site's domain. This is where `STATIC_ROOT` and `collectstatic` are useful outside of Django. You can set a reverse proxy to serve all the files that Django collects into `STATIC_ROOT`.
The process is roughly:
-1. Run `collectstatic`
- to put files into `STATIC_ROOT`.
-2. Configure the reverse proxy
- to handle any URL pattern
- that starts with `STATIC_URL`
- (recall `/static/` as an example)
- and pass those requests
- to the directory structure
- of `STATIC_ROOT`.
-3. Anything that doesn't look
- like a static file (e.g., `/accounts/login/`)
- is delegated
- to the app server running Django.
-
-In this setup,
-the Django app never has to worry
-about serving static files
-because the reverse proxy
-takes care of those requests
-before reaching the app server.
-The performance boost comes
-from the reverse proxy itself.
-Most reverse proxies are designed
-in very high performance languages
-like C
-because they are designed
-to handle a specific problem:
-routing requests.
-This flow lets Django handle the dynamic requests
-that it needs to
-and prevents the slower Python processes
-from doing work
-that reverse proxies are built for.
-
-If this kind of setup appeals
-to you,
-one such reverse proxy
-that you can consider is
-{{< extlink "https://www.nginx.com/" "Nginx" >}}.
-The configuration of Nginx is beyond the scope
+1. Run `collectstatic` to put files into `STATIC_ROOT`.
+2. Configure the reverse proxy to handle any URL pattern that starts with `STATIC_URL` (recall `/static/` as an example) and pass those requests to the directory structure of `STATIC_ROOT`.
+3. Anything that doesn't look like a static file (e.g., `/accounts/login/`) is delegated to the app server running Django.
+
+In this setup, the Django app never has to worry about serving static files because the reverse proxy takes care of those requests before reaching the app server. The performance boost comes from the reverse proxy itself. Most reverse proxies are designed in very high performance languages like C because they are designed to handle a specific problem: routing requests. This flow lets Django handle the dynamic requests that it needs to and prevents the slower Python processes from doing work that reverse proxies are built for.
+
+If this kind of setup appeals to you, one such reverse proxy that you can consider is {{< extlink "https://www.nginx.com/" "Nginx" >}}. The configuration of Nginx is beyond the scope
{{< web >}}
of this series,
{{< /web >}}
{{< book >}}
of this book,
{{< /book >}}
-but there are plenty
-of solid tutorials
-that will show how to configure a Django app
-with Nginx.
+but there are plenty of solid tutorials that will show how to configure a Django app with Nginx.
## Summary
@@ -763,9 +298,7 @@ We looked at:
* How to configure static files
* The way to work with static files
-* How to handle static files
- when deploying your site
- to the internet
+* How to handle static files when deploying your site to the internet
{{< web >}}
Looking ahead to the next article,
@@ -773,28 +306,15 @@ Looking ahead to the next article,
{{< book >}}
In the next chapter,
{{< /book >}}
-we will learn about automated testing
-for your Django applications.
-Testing is one of my favorite topics
-so I'm excited to share with you
-about it.
+we will learn about automated testing for your Django applications. Testing is one of my favorite topics so I'm excited to share with you about it.
+
We'll cover:
* Why would anyone want to write automated tests
-* What kinds of tests are useful
- to a Django app
+* What kinds of tests are useful to a Django app
* What tools can you use to make testing easier
{{< web >}}
-If you'd like to follow along
-with the series,
-please feel free to sign up
-for my newsletter
-where I announce all of my new content.
-If you have other questions,
-you can reach me online
-on Twitter
-where I am
-{{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
+If you'd like to follow along with the series, please feel free to sign up for my newsletter where I announce all of my new content. If you have other questions, you can reach me online on Twitter where I am {{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
{{< /web >}}
From e07f12b7e83cbbc70cad32be2557388caad7c633 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nazar=C3=A9=20da=20Piedade?=
Date: Fri, 5 May 2023 11:18:13 +0100
Subject: [PATCH 13/30] chore: format
`understand-django/2021-02-22-test-your-apps.pt.md`
---
.../2021-02-22-test-your-apps.pt.md | 987 +++---------------
1 file changed, 163 insertions(+), 824 deletions(-)
diff --git a/content/understand-django/2021-02-22-test-your-apps.pt.md b/content/understand-django/2021-02-22-test-your-apps.pt.md
index 5704dd41..b2c69b72 100644
--- a/content/understand-django/2021-02-22-test-your-apps.pt.md
+++ b/content/understand-django/2021-02-22-test-your-apps.pt.md
@@ -1,14 +1,7 @@
---
title: "Test Your Apps"
description: >-
- How do you confirm that your website works?
- You could click around
- and check things out yourself,
- or you can write code to verify the site.
- I'll show you why you should prefer the latter.
- In this Understand Django article,
- we'll study automated tests
- to verify the correctness of your site.
+ How do you confirm that your website works? You could click around and check things out yourself, or you can write code to verify the site. I'll show you why you should prefer the latter. In this Understand Django article, we'll study automated tests to verify the correctness of your site.
image: img/django.png
type: post
categories:
@@ -24,299 +17,82 @@ series: "Understand Django"
In the previous
{{< web >}}
-[Understand Django]({{< ref "/understand-django/_index.md" >}})
-article,
+[Understand Django]({{< ref "/understand-django/_index.pt.md" >}}) article,
{{< /web >}}
{{< book >}}
chapter,
{{< /book >}}
-we saw how static files
-like CSS, JavaScript, and images
-can be incorporated
-into your site.
-Now we're going to focus
-on how to verify
-that your website works
-and continues to work
-by writing automated tests
-that check your pages
-and your code logic.
-
-{{< understand-django-series "tests" >}}
+we saw how static files like CSS, JavaScript, and images can be incorporated into your site. Now we're going to focus on how to verify that your website works and continues to work by writing automated tests that check your pages and your code logic.
+
+{{< understand-django-series-pt "tests" >}}
## Why Write Tests
-I'm going to assume
-that if you're reading this,
-then you've either got a Django project
-or are considering working
-with Django
-to build a project.
-If that's true,
-think about your project
-and how you would make sure it works.
-
-When you start out with a project,
-whether for a tutorial
-or for something real
-that you plan to grow,
-the fledgling site has very little functionality.
-To check that the site is working,
-you can start up the local web server,
-open your browser,
-navigate to the `localhost` URL,
-and confirm that the site is functional.
-How long does that take?
-5 seconds?
-15 seconds?
-30 seconds?
-
-For starting out,
-manually checking out your site is fine.
-What happens, though,
-when you create more pages?
-How do you continue to confirm
-that all your pages are functional?
-You could open up the local site
-and start clicking around,
-but the time spent confirming
-that everything works begins to grow.
-Maybe your verification effort takes 3 minutes, 5 minutes, or perhaps much more.
-If you're not careful,
-your creation may start to feel
-like the mythical multi-headed Hydra,
-and what once was a fun project
-to work on
-devolves into a chore
-of tedious page verification.
-
-You can't eliminate the fact
-that a larger project means
-that there is more to check.
-What you *can* do is change the name
-of the game.
-You can change your page checking
-from something manual
-that may take 15 seconds to verify a page
-to something that a computer can do
-in *milliseconds*.
-
-This is where automated tests come
-into the picture.
-Automated tests let computers do what computers do best:
-run repetitive tasks repeatedly, consistently,
-and quickly.
-When we write tests,
-our goal is to confirm some logic or behavior
-in a deterministic way.
-
-Let's look at a test
-for a hypothetical `add` function
-which functions
-like the `+` operator.
-This should give us a feel
-for what an automated test is like
-if you've never encountered tests before.
+I'm going to assume that if you're reading this, then you've either got a Django project or are considering working with Django to build a project. If that's true, think about your project and how you would make sure it works.
+
+When you start out with a project, whether for a tutorial or for something real that you plan to grow, the fledgling site has very little functionality. To check that the site is working, you can start up the local web server, open your browser, navigate to the `localhost` URL, and confirm that the site is functional. How long does that take? 5 seconds? 15 seconds? 30 seconds?
+
+For starting out, manually checking out your site is fine. What happens, though, when you create more pages? How do you continue to confirm that all your pages are functional? You could open up the local site and start clicking around, but the time spent confirming that everything works begins to grow. Maybe your verification effort takes 3 minutes, 5 minutes, or perhaps much more. If you're not careful, your creation may start to feel like the mythical multi-headed Hydra, and what once was a fun project to work on devolves into a chore of tedious page verification.
+
+You can't eliminate the fact that a larger project means that there is more to check. What you *can* do is change the name of the game. You can change your page checking from something manual that may take 15 seconds to verify a page to something that a computer can do in *milliseconds*.
+
+This is where automated tests come into the picture. Automated tests let computers do what computers do best: run repetitive tasks repeatedly, consistently, and quickly. When we write tests, our goal is to confirm some logic or behavior in a deterministic way.
+
+Let's look at a test for a hypothetical `add` function which functions like the `+` operator. This should give us a feel for what an automated test is like if you've never encountered tests before:
```python
def test_does_it_add():
assert add(40, 2) == 42
```
-The test works by running the code
-and comparing the result
-to whatever we expect that result to be.
-The test *asserts* that the equality statement is true.
-If the equality is false,
-then the assertion raises an exception
-and the test fails.
-
-This automated test would take virtually no time to run
-if you compared it to running the function
-in a Python REPL
-to inspect the result manually.
-
-Seeing a silly example
-of an `add` function doesn't really help you much
-with how you should test your Django project.
-Next,
-we'll look at some types
-of tests
-for Django.
-If you add these kinds of tests
-to your project,
-you'll be able to make changes
-to your website
-with more confidence
-that you're not breaking things.
+The test works by running the code and comparing the result to whatever we expect that result to be. The test *asserts* that the equality statement is true. If the equality is false, then the assertion raises an exception and the test fails.
+
+This automated test would take virtually no time to run if you compared it to running the function in a Python REPL to inspect the result manually.
+
+Seeing a silly example of an `add` function doesn't really help you much with how you should test your Django project. Next, we'll look at some types of tests for Django. If you add these kinds of tests to your project, you'll be able to make changes to your website with more confidence that you're not breaking things.
## Useful Types Of Django Tests
-When we explored the anatomy
-of a Django application,
-I noted that I *always* delete the `tests.py` file
-that comes with the `startapp` command.
-The reason I do this is
-because there are different kinds
-of tests,
-and I want those different kinds
-to live in separate files.
-My apps have those separate files
-in a `tests` package
-within the app
-instead of a `tests.py` module.
-
-My `tests` package will often mirror the structure
-of the application itself.
-The program which executes tests,
-which is called a "test runner,"
-typically expects to find tests
-in files that start with `test_`.
-The package often includes:
+When we explored the anatomy of a Django application, I noted that I *always* delete the `tests.py` file that comes with the `startapp` command. The reason I do this is because there are different kinds of tests, and I want those different kinds to live in separate files. My apps have those separate files in a `tests` package within the app instead of a `tests.py` module.
+
+My `tests` package will often mirror the structure of the application itself. The program which executes tests, which is called a "test runner," typically expects to find tests in files that start with `test_`. The package often includes:
* `test_forms.py`
* `test_models.py`
* `test_views.py`
* etc.
-This structure hints
-at the kinds
-of tests
-that you'd write
-for your application,
-but I'll touch on specifics more a bit later.
-Broadly,
-when we write automated tests,
-there is an important dimension
-to consider:
-how much application code should my test run?
-
-The answer to that question influences the behavior
-of tests.
-If we write a test
-that runs a lot of code,
-then we benefit
-by checking a lot of a system
-at once; however,
-there are some downsides:
-
-* Running a lot of code means more things can happen
- and there is a higher chance
- of your test breaking
- in unexpected ways.
- A test that often breaks
- in unexpected ways is called a "brittle" test.
-* Running a lot of code means that there is a lot
- of code to run.
- That's axiomatic,
- but the implication is that a test
- with more code to execute will take longer
- to run.
- Big automated tests are still very likely
- to be much faster
- than the same test executed manually,
- so running time is relative.
-
-When we have tests
-that run many parts
-of your application
-that are *integrated* together,
-we call these tests *integration tests*.
-Integration tests are good at surfacing issues related
-to the *connections between code*.
-For instance,
-if you called a method
-and passed in the wrong arguments,
-an integration test is likely
-to discover that problem.
-
-On the other end of the spectrum
-are tests that run very little code.
-The `add` test from above is a good example.
-These kinds of tests check individual units
-of code
-(e.g., a Django model).
-For that reason,
-we call these *unit tests*.
-Unit tests are good at *checking a piece
-of code
-in isolation*
-to confirm its behavior.
-
-Unit tests have downsides too.
-These tests execute
-without a lot of context
-from the rest
-of an application.
-This can help you confirm the behavior
-of the piece,
-but it might not be the behavior
-that the larger application requires.
-
-In this explanation,
-the lesson is that both kinds
-of tests are good,
-yet have tradeoffs.
-Beware of anyone who tells you
-that you should only write one kind
-of test
-or the other.
-
-> A good set of automated tests will include both **unit** and **integration** tests
-to check behavior
-of the individual units
-and the interconnections
-between parts.
-
-We have to consider another aspect
-to this discussion:
-what is the "right" amount
-of code for a unit test?
-*There's no absolutely correct answer here.*
-In fact,
-this topic is hotly debated
-among testers.
-
-Some people will assert
-that a unit test should only run the code
-for that unit.
-If you have a class
-that implements some pure logic
-and doesn't need other code,
-then you're in the ideal case.
-But what happens if you're testing
-a method that you added to a Django model
-that needs to interact
-with a database?
-Even if the only thing you're testing is the individual model method,
-a unit test purist would highlight
-that the test is actually an integration test
-if it interacts with a database.
-
-**I usually find this kind of discussion counterproductive.**
-In my experience,
-this sort of philosophical debate
-about what is a unit test doesn't typically help
-with testing your web app
-to verify its correctness.
-I brought all of this up because,
-if you're going to learn more about testing
+This structure hints at the kinds of tests that you'd write for your application, but I'll touch on specifics more a bit later. Broadly, when we write automated tests, there is an important dimension to consider: how much application code should my test run?
+
+The answer to that question influences the behavior of tests. If we write a test that runs a lot of code, then we benefit by checking a lot of a system at once; however, there are some downsides:
+
+* Running a lot of code means more things can happen and there is a higher chance of your test breaking in unexpected ways. A test that often breaks in unexpected ways is called a "brittle" test.
+* Running a lot of code means that there is a lot of code to run. That's axiomatic, but the implication is that a test with more code to execute will take longer to run. Big automated tests are still very likely to be much faster than the same test executed manually, so running time is relative.
+
+When we have tests that run many parts of your application that are *integrated* together, we call these tests *integration tests*. Integration tests are good at surfacing issues related to the *connections between code*. For instance, if you called a method and passed in the wrong arguments, an integration test is likely to discover that problem.
+
+On the other end of the spectrum are tests that run very little code. The `add` test from above is a good example. These kinds of tests check individual units of code (e.g., a Django model). For that reason, we call these *unit tests*. Unit tests are good at *checking a piece of code in isolation* to confirm its behavior.
+
+Unit tests have downsides too. These tests execute without a lot of context from the rest of an application. This can help you confirm the behavior of the piece, but it might not be the behavior that the larger application requires.
+
+In this explanation, the lesson is that both kinds of tests are good, yet have tradeoffs. Beware of anyone who tells you that you should only write one kind of test or the other.
+
+> A good set of automated tests will include both **unit** and **integration** tests to check behavior of the individual units and the interconnections between parts.
+
+We have to consider another aspect to this discussion: what is the "right" amount of code for a unit test? *There's no absolutely correct answer here.* In fact, this topic is hotly debated among testers.
+
+Some people will assert that a unit test should only run the code for that unit. If you have a class that implements some pure logic and doesn't need other code, then you're in the ideal case. But what happens if you're testing a method that you added to a Django model that needs to interact with a database? Even if the only thing you're testing is the individual model method, a unit test purist would highlight that the test is actually an integration test if it interacts with a database.
+
+**I usually find this kind of discussion counterproductive.** In my experience, this sort of philosophical debate about what is a unit test doesn't typically help with testing your web app to verify its correctness. I brought all of this up because, if you're going to learn more about testing
{{< web >}}
after this article,
{{< /web >}}
{{< book >}}
after this chapter,
{{< /book >}}
-I caution you
-to avoid getting sucked
-into this definition trap.
-
-Here are my working definitions
-of unit and integration tests
-in Django.
-These definitions are imperfect
-(as are *any* definitions),
-but they should help frame the discussion
+I caution you to avoid getting sucked into this definition trap.
+
+Here are my working definitions of unit and integration tests in Django. These definitions are imperfect (as are *any* definitions), but they should help frame the discussion
{{< web >}}
in this article.
{{< /web >}}
@@ -324,114 +100,37 @@ in this article.
in this chapter.
{{< /book >}}
-* **Unit tests** - Tests that check individual units
- within a Django project like a model method
- or a form.
-* **Integration test** - Tests that check a group of units
- and their interactions
- like checking if a view renders the expected output.
+* **Unit tests** - Tests that check individual units within a Django project like a model method or a form.
+* **Integration test** - Tests that check a group of units and their interactions like checking if a view renders the expected output.
-Now that we have some core notion
-of what tests are about,
-let's get into the details.
+Now that we have some core notion of what tests are about, let's get into the details.
### Unit Tests
-As we get into some examples,
-I need to introduce a couple
-of tools
-that I use
-on all of my Django projects.
-I'll describe these tools
-in more depth
-in a later section,
-but they need a brief introduction here
-or my examples won't make much sense.
-My two "must have" packages are:
+As we get into some examples, I need to introduce a couple of tools that I use on all of my Django projects. I'll describe these tools in more depth in a later section, but they need a brief introduction here or my examples won't make much sense. My two "must have" packages are:
* `pytest-django`
* `factory-boy`
-`pytest-django` is a package
-that makes it possible
-to run Django tests
-through the `pytest` program.
-pytest is an extremely popular Python testing tool
-with a huge ecosystem
-of extensions.
-In fact,
-`pytest-django` is one
-of those extensions.
-
-My biggest reason
-for using `pytest-django` is
-that it lets me use the `assert` keyword
-in all of my tests.
-In the Python standard library's `unittest` module
-and, by extension,
-Django's built-in test tools
-which subclass `unittest` classes,
-checking values requires methods
-like `assertEqual`
-and `assertTrue`.
-As we'll see,
-using the `assert` keyword exclusively is a very natural way
-to write tests.
-
-The other vital tool
-in my tool belt is `factory-boy`.
-PyPI calls this `factory-boy`,
-but the documentation uses `factory_boy`,
-so we'll use that naming from here on.
-`factory_boy` is a tool
-for building test database data.
-The library has fantastic Django integration
-and gives us the ability
-to generate model data
-with ease.
-
-Again,
-I'll focus on these two packages later on
-to cover more of their features,
-but you'll see them used immediately
-in the examples.
+`pytest-django` is a package that makes it possible to run Django tests through the `pytest` program. pytest is an extremely popular Python testing tool with a huge ecosystem of extensions. In fact, `pytest-django` is one of those extensions.
+
+My biggest reason for using `pytest-django` is that it lets me use the `assert` keyword in all of my tests. In the Python standard library's `unittest` module and, by extension, Django's built-in test tools which subclass `unittest` classes, checking values requires methods like `assertEqual` and `assertTrue`. As we'll see, using the `assert` keyword exclusively is a very natural way to write tests.
+
+The other vital tool in my tool belt is `factory-boy`. PyPI calls this `factory-boy`, but the documentation uses `factory_boy`, so we'll use that naming from here on. `factory_boy` is a tool for building test database data. The library has fantastic Django integration and gives us the ability to generate model data with ease.
+
+Again, I'll focus on these two packages later on to cover more of their features, but you'll see them used immediately in the examples.
#### Model Tests
-In Django projects,
-we use models
-to hold data
-about our app,
-so it's very natural
-to add methods
-to the models
-to interact
-with the data.
-How do we write a test
-that checks that the method
-does what we expect?
-
-I'm going to give you a mental framework
-for *any* of your tests,
-not only unit tests.
-This framework should help you reason
-through any tests
-that you encounter
-when reading and writing code.
-The framework is the *AAA pattern*.
-The AAA patterns stands for:
-
-* **Arrange** - This is the part of the test
- that sets up your data
- and any necessary preconditions
- for your test.
-* **Act** - This stage is when your test runs the application code
- that you want to test.
-* **Assert** - The last part checks
- that your action is what you expected.
-
-For a model test,
-this looks like:
+In Django projects, we use models to hold data about our app, so it's very natural to add methods to the models to interact with the data. How do we write a test that checks that the method does what we expect?
+
+I'm going to give you a mental framework for *any* of your tests, not only unit tests. This framework should help you reason through any tests that you encounter when reading and writing code. The framework is the *AAA pattern*. The AAA patterns stands for:
+
+* **Arrange** - This is the part of the test that sets up your data and any necessary preconditions for your test.
+* **Act** - This stage is when your test runs the application code that you want to test.
+* **Assert** - The last part checks that your action is what you expected.
+
+For a model test, this looks like:
```python
# application/tests/test_models.py
@@ -452,89 +151,32 @@ class TestOrder:
assert order.status == Order.Status.SHIPPED
```
-We can imagine a project
-that includes an ecommerce system.
-A big part
-of handling orders is tracking status.
-We could manually set the status field
-throughout the app,
-but changing status
-within a method gives us the chance
-to do other things.
-For instance,
-maybe the `ship` method also triggers sending an email.
-
-In the test above,
-we're checking the state transition
-from `PENDING` to `SHIPPED`.
-The test acts on the `ship` method,
-then refreshes the model instance
-from the database
-to ensure that the `SHIPPED` status persisted.
-
-What are some good qualities
-about this test?
-
-The test includes a docstring.
-Trust me, you *will* benefit from docstrings
-on your tests.
-There is a strong temptation to leave things
-at `test_shipped`,
-but in the future you may not have enough context.
-
-Many developers opt for long test names instead.
-While I have no problem with long descriptive test names,
-docstrings are helpful too.
-Whitespace is a *good* thing
-and, in my opinion, it's easier to read
-"The widget updates the game state when pushed."
-than `test_widget_updates_game_state_when_pushed`.
-
-The test checks one action.
-A test that checks a single action can fit
-in your head.
-There's no question about interaction
-with other parts.
-There's also no question about what is actually being tested.
-The simplicity of testing a single action makes each unit test
+We can imagine a project that includes an ecommerce system. A big part of handling orders is tracking status. We could manually set the status field throughout the app, but changing status within a method gives us the chance to do other things. For instance, maybe the `ship` method also triggers sending an email.
+
+In the test above, we're checking the state transition from `PENDING` to `SHIPPED`. The test acts on the `ship` method, then refreshes the model instance from the database to ensure that the `SHIPPED` status persisted.
+
+What are some good qualities about this test?
+
+The test includes a docstring. Trust me, you *will* benefit from docstrings on your tests. There is a strong temptation to leave things at `test_shipped`, but in the future you may not have enough context.
+
+Many developers opt for long test names instead. While I have no problem with long descriptive test names, docstrings are helpful too. Whitespace is a *good* thing and, in my opinion, it's easier to read "The widget updates the game state when pushed." than `test_widget_updates_game_state_when_pushed`.
+
+The test checks one action. A test that checks a single action can fit in your head. There's no question about interaction with other parts. There's also no question about what is actually being tested. The simplicity of testing a single action makes each unit test
tell a unique story.
-Conversely,
-you'll likely encounter tests
-in projects
-that do a lot of initial arrangement,
-then alternate between act and assert lines
-in a single test.
-These kinds of tests are brittle
-(i.e., the term to indicate that the test can break
-and fail easily)
-and are difficult to understand
-when there is a failure.
-
-The qualities in this test translate
-to lots of different test types.
-I think that's the beauty
-of having a solid mental model
-for testing.
-Once you see the way
-that tests:
+Conversely, you'll likely encounter tests in projects that do a lot of initial arrangement, then alternate between act and assert lines in a single test. These kinds of tests are brittle (i.e., the term to indicate that the test can break and fail easily) and are difficult to understand when there is a failure.
+
+The qualities in this test translate to lots of different test types. I think that's the beauty of having a solid mental model for testing. Once you see the way that tests:
1. Set up the inputs,
2. Take action,
3. Check the outputs,
-then automated testing becomes a lot less scary
-and more valuable to you.
-Now let's see how this same pattern plays out
-in forms.
+then automated testing becomes a lot less scary and more valuable to you. Now let's see how this same pattern plays out in forms.
#### Form Tests
-When writing tests,
-we often want to write a "happy path" test.
-This kind of test is when everything works exactly
-as you hope.
-This is a happy path form test.
+When writing tests, we often want to write a "happy path" test. This kind of test is when everything works exactly as you hope. This is a happy path form test:
```python
# application/tests/test_forms.py
@@ -560,9 +202,7 @@ class TestSupportForm:
).count() == 1
```
-With this test,
-we are synthesizing a POST request.
-The test:
+With this test, we are synthesizing a POST request. The test:
* Builds the POST data as `data`
* Creates a bound form (i.e., connects `data=data` in the constructor)
@@ -570,26 +210,9 @@ The test:
* Saves the form
* Asserts that a new record was created
-Notice that I'm bending the AAA rules a bit
-for this test.
-Part of the Django convention
-for forms
-is that the form is valid
-before calling the `save` method.
-If that convention is not followed,
-then `cleaned_data` won't be populated correctly
-and most `save` methods depend on `cleaned_data`.
-Even though `is_valid` is an action,
-I view it as a setup step
-for form tests.
-
-When we work with forms,
-a lot of what we care about is cleaning the data
-to make sure
-that junk is not getting
-into your app's database.
-Let's write a test
-for an invalid form.
+Notice that I'm bending the AAA rules a bit for this test. Part of the Django convention for forms is that the form is valid before calling the `save` method. If that convention is not followed, then `cleaned_data` won't be populated correctly and most `save` methods depend on `cleaned_data`. Even though `is_valid` is an action, I view it as a setup step for form tests.
+
+When we work with forms, a lot of what we care about is cleaning the data to make sure that junk is not getting into your app's database. Let's write a test for an invalid form:
```python
# application/tests/test_forms.py
@@ -614,97 +237,40 @@ class TestSupportForm:
assert 'email' in form.errors
```
-The test shows the mechanics
-for checking an invalid form.
-The key elements are:
+The test shows the mechanics for checking an invalid form. The key elements are:
* Set up the bad form data
* Check the validity with `is_valid`
* Inspect the output state in `form.errors`
-This test shows how to check an invalid form,
-but I'm less likely to write this particular test
-in a real project.
-Why?
-Because the test is checking functionality
-from Django's `EmailField`
-which has the validation logic
-to know what is a real email or not.
-
-Generally,
-I don't think it's valuable
-to test features
-from the framework itself.
-A good open source project
-like Django
-is already testing those features
-for you.
-When you write form tests,
-you should check on custom `clean_*` and `clean` methods
-as well as any custom `save` method
-that you might add.
-
-The patterns for both happy path and error cases are what I use
-for virtually all
-of my Django form tests.
-Let's move on
-to the integration tests
-to see what it looks like
-to test more code
-at once.
+This test shows how to check an invalid form, but I'm less likely to write this particular test in a real project. Why? Because the test is checking functionality from Django's `EmailField` which has the validation logic to know what is a real email or not.
+
+Generally, I don't think it's valuable to test features from the framework itself. A good open source project like Django is already testing those features for you. When you write form tests, you should check on custom `clean_*` and `clean` methods as well as any custom `save` method that you might add.
+
+The patterns for both happy path and error cases are what I use for virtually all of my Django form tests. Let's move on to the integration tests to see what it looks like to test more code at once.
### Integration Tests
-In my opinion,
-a good integration test won't look very different
-from a good unit test.
-An integration test can still follow the AAA pattern
-like other automated tests.
-The parts that change are the tools you'll use
-and the assertions you will write.
-
-My definition of an integration test
-in Django
-is a test
-that uses Django's test `Client`.
+In my opinion, a good integration test won't look very different from a good unit test. An integration test can still follow the AAA pattern like other automated tests. The parts that change are the tools you'll use and the assertions you will write.
+
+My definition of an integration test in Django is a test that uses Django's test `Client`.
{{< web >}}
In previous articles,
{{< /web >}}
{{< book >}}
In previous chapters,
{{< /book >}}
-I've only mentioned what a client is in passing.
-In the context
-of a web application,
-a client is anything that consumes the output
-of a web app
-to display it to a user.
-
-The most obvious client for a web app
-is a web browser,
-but there are plenty
-of other client types out there.
-Some examples that could use output
-from a web application:
+I've only mentioned what a client is in passing. In the context of a web application, a client is anything that consumes the output of a web app to display it to a user.
+
+The most obvious client for a web app is a web browser, but there are plenty of other client types out there. Some examples that could use output from a web application:
* A native mobile application
* A command line interface
-* A programming library like Python's `requests` package
- that can handle HTTP requests and responses
-
-The Django test `Client` is like these other clients
-in that it can interact
-with your Django project
-to receive data from requests
-that it creates.
-The nice part about the test client is that the output
-is returned in a convenient way
-that we can assert against.
-The client returns the `HttpResponse` object directly!
-
-With that context,
-here's an integration test
-that we can discuss.
+* A programming library like Python's `requests` package that can handle HTTP requests and responses
+
+The Django test `Client` is like these other clients in that it can interact with your Django project to receive data from requests that it creates. The nice part about the test client is that the output is returned in a convenient way that we can assert against. The client returns the `HttpResponse` object directly!
+
+With that context, here's an integration test that we can discuss:
```python
# application/tests/test_views.py
@@ -728,133 +294,44 @@ class TestProfileView:
assert user.first_name in response.content.decode()
```
-What is this test doing?
-Also, what is this test *not* doing?
+What is this test doing? Also, what is this test *not* doing?
-By using the Django test client,
-the test runs a lot of Django code.
-This goes through:
+By using the Django test client, the test runs a lot of Django code. This goes through:
* URL routing
* View execution (which will likely fetch from the database)
* Template rendering
-That's a lot of code to execute
-in a single test!
-The goal of the test is
-to check that all the major pieces hang together.
-
-Now let's observe what the test is not doing.
-Even though the test runs a ton of code,
-there aren't a huge number of `assert` statements.
-In other words,
-our goal with an integration isn't
-to check every tiny little thing
-that could happen in the whole flow.
-Hopefully, we have unit tests
-that cover those little parts
-of the system.
-
-When I write an integration test,
-I'm mostly trying to answer the question:
-**does the *system* hold together without breaking?**
-
-Now that we've covered unit tests
-and integration tests,
-what are some tools that will help you make testing easier?
+That's a lot of code to execute in a single test! The goal of the test is to check that all the major pieces hang together.
+
+Now let's observe what the test is not doing. Even though the test runs a ton of code, there aren't a huge number of `assert` statements. In other words, our goal with an integration isn't to check every tiny little thing that could happen in the whole flow. Hopefully, we have unit tests that cover those little parts of the system.
+
+When I write an integration test, I'm mostly trying to answer the question: **does the *system* hold together without breaking?**
+
+Now that we've covered unit tests and integration tests, what are some tools that will help you make testing easier?
## Tools To Help
-When testing your application,
-you have access to so many packages
-to help
-that it can be fairly overwhelming.
-If you're testing
-for the first time,
-you may be struggling
-with applying the AAA pattern
-and knowing what to test.
-We want to minimize the extra stuff
-that you have to know.
-
-We're going to revisit the tools
-that I listed earlier, `pytest-django` and `factory_boy`,
-to get you started.
-Consider these your Django testing survival kit.
-As you develop your testing skills,
-you can add more tools
-to your toolbox,
-but these two tools are a fantastic start.
+When testing your application, you have access to so many packages to help that it can be fairly overwhelming. If you're testing for the first time, you may be struggling with applying the AAA pattern and knowing what to test. We want to minimize the extra stuff that you have to know.
+
+We're going to revisit the tools that I listed earlier, `pytest-django` and `factory_boy`, to get you started. Consider these your Django testing survival kit. As you develop your testing skills, you can add more tools to your toolbox, but these two tools are a fantastic start.
### `pytest-django`
-{{< extlink "https://docs.pytest.org/en/stable/" "pytest" >}} is a "test runner."
-The tool's job is to run automated tests.
-If you read {{< extlink "https://docs.djangoproject.com/en/4.1/topics/testing/overview/" "Writing and running tests" >}}
-in the Django documentation,
-you'll discover
-that Django *also* includes a test runner
-with `./manage.py test`.
-What gives?
-Why am I suggesting that you use `pytest`?
-
-I'm going to make a bold assertion:
-**pytest is better**.
-(Did I just go meta there? Yes, I did. 😆)
-
-I like a lot about Django's built-in test runner,
-but I keep coming back to pytest
-for one primary reason:
-I can use `assert` in tests.
-As you've seen
-in these test examples,
-the `assert` keyword makes for clear reading.
-We can use all of Python's normal comparison tests
-(e.g., `==`, `!=`, `in`)
-to check the output
-of tests.
-
-Django's test runner builds off the test tools
-that are included
-with Python
-in the `unittest` module.
-With those test tools,
-developers must make test classes
-that subclass `unittest.TestCase`.
-The downside of `TestCase` classes is
-that you must use a set of `assert*` methods
-to check your code.
-
-The list of `assert*` methods are included
-in the {{< extlink "https://docs.python.org/3/library/unittest.html#assert-methods" "unittest" >}} documentation.
-You can be very successful
-with these methods,
-but I think it requires remembering an API
-that includes a large number
-of methods.
-Consider this.
-Would you rather:
+{{< extlink "https://docs.pytest.org/en/stable/" "pytest" >}} is a "test runner." The tool's job is to run automated tests. If you read {{< extlink "https://docs.djangoproject.com/en/4.1/topics/testing/overview/" "Writing and running tests" >}} in the Django documentation, you'll discover that Django *also* includes a test runner with `./manage.py test`. What gives? Why am I suggesting that you use `pytest`?
+
+I'm going to make a bold assertion: **pytest is better**. (Did I just go meta there? Yes, I did. 😆)
+
+I like a lot about Django's built-in test runner, but I keep coming back to pytest for one primary reason: I can use `assert` in tests. As you've seen in these test examples, the `assert` keyword makes for clear reading. We can use all of Python's normal comparison tests (e.g., `==`, `!=`, `in`) to check the output of tests.
+
+Django's test runner builds off the test tools that are included with Python in the `unittest` module. With those test tools, developers must make test classes that subclass `unittest.TestCase`. The downside of `TestCase` classes is that you must use a set of `assert*` methods to check your code.
+
+The list of `assert*` methods are included in the {{< extlink "https://docs.python.org/3/library/unittest.html#assert-methods" "unittest" >}} documentation. You can be very successful with these methods, but I think it requires remembering an API that includes a large number of methods. Consider this. Would you rather:
1. Use `assert`? OR
-2. Use `assertEqual`,
- `assertNotEqual`,
- `assertTrue`,
- `assertFalse`,
- `assertIs`,
- `assertIsNot`,
- `assertIsNone`,
- `assertIsNotNone`,
- `assertIn`,
- `assertNotIn`,
- `assertIsInstance`,
- and `assertNotIsInstance`?
-
-Using `assert` from pytest means
-that you get all the benefits
-of the `assert*` methods,
-but you only need to remember a single keyword.
-If that wasn't enough,
-let's compare the readability:
+2. Use `assertEqual`, `assertNotEqual`, `assertTrue`, `assertFalse`, `assertIs`, `assertIsNot`, `assertIsNone`, `assertIsNotNone`, `assertIn`, `assertNotIn`, `assertIsInstance`, and `assertNotIsInstance`?
+
+Using `assert` from pytest means that you get all the benefits of the `assert*` methods, but you only need to remember a single keyword. If that wasn't enough, let's compare the readability:
```python
self.assertEqual(my_value, 42)
@@ -870,71 +347,25 @@ self.assertTrue(my_value)
assert my_value
```
-For the same reason
-that Python developers prefer `property` methods
-instead of getters and setters
-(e.g. `obj.value = 42` instead of `obj.set_value(42)`),
-I think the `assert` style syntax is far simpler
-to visually process.
+For the same reason that Python developers prefer `property` methods instead of getters and setters (e.g. `obj.value = 42` instead of `obj.set_value(42)`), I think the `assert` style syntax is far simpler to visually process.
-Outside of the awesome handling of `assert`,
-{{< extlink "https://pytest-django.readthedocs.io/en/latest/" "pytest-django" >}}
-includes a lot of other features
-that you might find interesting
-when writing automated tests.
+Outside of the awesome handling of `assert`, {{< extlink "https://pytest-django.readthedocs.io/en/latest/" "pytest-django" >}} includes a lot of other features that you might find interesting when writing automated tests.
### `factory_boy`
-The other test package
-that I think every developer should use
-in their Django projects is
-`{{< extlink "https://factoryboy.readthedocs.io/en/stable/" "factory_boy" >}}`.
+The other test package that I think every developer should use in their Django projects is `{{< extlink "https://factoryboy.readthedocs.io/en/stable/" "factory_boy" >}}`.
> `factory_boy` helps you build model data for your tests.
-The recommendation when writing automated tests
-is to use an empty test database.
-If fact,
-the common pattern provided with Django testing tools
-is to use an empty database
-for every test.
-Having a blank slate in the database helps
-each test be independent
-and makes it easier to assert
-on the state of the database.
-Because the test database is empty,
-you'll need a strategy to populate your tests
-with the appropriate data to check against.
-
-As you build up your Django project,
-you will have more models
-that help to describe the domain
-that your website addresses.
-Generating model data
-for your tests is an
-immensely valuable capability.
-
-You *could* use your model manager's `create` method
-to create a database entry
-for your test,
-but you're going to run into some limits very fast.
-
-The biggest challenge with using `create` comes
-from database constraints like foreign keys.
-What do you do if you want to build a record
-that requires a large number
-of non-nullable foreign key relationships?
-Your only choice is to create those foreign key records.
-
-We can imagine an app
-that shows information about movies.
-The `Movie` model could have a variety
-of foreign key relationships
-like director, producer, studio,
-and so on.
-I'll use a few in the example,
-but imagine what would happen
-as the number of foreign key relationships increases.
+The recommendation when writing automated tests is to use an empty test database. If fact, the common pattern provided with Django testing tools is to use an empty database for every test. Having a blank slate in the database helps each test be independent and makes it easier to assert on the state of the database. Because the test database is empty, you'll need a strategy to populate your tests with the appropriate data to check against.
+
+As you build up your Django project, you will have more models that help to describe the domain that your website addresses. Generating model data for your tests is an immensely valuable capability.
+
+You *could* use your model manager's `create` method to create a database entry for your test, but you're going to run into some limits very fast.
+
+The biggest challenge with using `create` comes from database constraints like foreign keys. What do you do if you want to build a record that requires a large number of non-nullable foreign key relationships? Your only choice is to create those foreign key records.
+
+We can imagine an app that shows information about movies. The `Movie` model could have a variety of foreign key relationships like director, producer, studio, and so on. I'll use a few in the example, but imagine what would happen as the number of foreign key relationships increases:
```python
def test_detail_view_show_genre(client):
@@ -963,22 +394,7 @@ def test_detail_view_show_genre(client):
assert 'Sci-Fi' in response.content.decode()
```
-On the surface,
-the test isn't *too* bad.
-I think that's mostly because I kept the modeling simple.
-What if the `Director`, `Producer`, or `Studio` models also had required foreign keys?
-We'd spend most
-of our effort
-on the Arrangement section
-of the test.
-Also,
-as we inspect the test,
-we get bogged down with unnecessary details.
-Did we need to know the names
-of the director, producer, and studio?
-No, we didn't need that for this test.
-Now,
-let's look at the `factory_boy` equivalent.
+On the surface, the test isn't *too* bad. I think that's mostly because I kept the modeling simple. What if the `Director`, `Producer`, or `Studio` models also had required foreign keys? We'd spend most of our effort on the Arrangement section of the test. Also, as we inspect the test, we get bogged down with unnecessary details. Did we need to know the names of the director, producer, and studio? No, we didn't need that for this test. Now, let's look at the `factory_boy` equivalent:
```python
def test_detail_view_show_genre(client):
@@ -993,25 +409,11 @@ def test_detail_view_show_genre(client):
assert 'Sci-Fi' in response.content.decode()
```
-`MovieFactory` seems like magic.
-Our test got to ignore all the other details.
-Now the test could focus entirely
-on the genre.
-
-Factories simplify the construction of database records.
-Instead of wiring the models together
-in the test,
-we move that wiring to the factory definition.
-The benefit is that our tests can use the plain style
-that we see in the second example.
-If we need to add a new foreign key
-to the model,
-only the factory has to be updated,
-not all your other tests
-that use that model.
-
-What might this `Movie` factory look like?
-The factory might be:
+`MovieFactory` seems like magic. Our test got to ignore all the other details. Now the test could focus entirely on the genre.
+
+Factories simplify the construction of database records. Instead of wiring the models together in the test, we move that wiring to the factory definition. The benefit is that our tests can use the plain style that we see in the second example. If we need to add a new foreign key to the model, only the factory has to be updated, not all your other tests that use that model.
+
+What might this `Movie` factory look like? The factory might be:
```python
# application/tests/factories.py
@@ -1038,52 +440,13 @@ class MovieFactory(factory.django.DjangoModelFactory):
genre = 'Action'
```
-This factory definition is very declarative.
-We declare what we want,
-and `factory_boy` figures out how to put it together.
-This quality leads to factories
-that you can reason about
-because you can focus
-on the what
-and not the how
-of model construction.
-
-The other noteworthy aspect is that the factories compose together.
-When we call `MovieFactory()`,
-`factory_boy` is missing data
-about everything
-so it must build all of that data.
-The challenge is that the `MovieFactory` doesn't know
-how to build a `Director`
-or any of the movie's foreign key relationships.
-Instead,
-the factory will delegate
-to *other* factories
-using the `SubFactory` attribute.
-By delegating to other factories,
-`factory_boy` can build the model
-and its entire tree
-of relationships
-with a single call.
-
-When we want to override the behavior
-of some of the generated data,
-we pass in the extra argument
-as I did in the second example
-by providing "Sci-Fi"
-as the `genre`.
-You can pass in other model instances
-to your factories too.
-
-`factory_boy` makes testing
-with database records a joy.
-In my experience,
-most of my Django tests require some amount
-of database data
-so I use factories very heavily.
-I think you will find
-that `factory_boy` is a worthy addition
-to your test tools.
+This factory definition is very declarative. We declare what we want, and `factory_boy` figures out how to put it together. This quality leads to factories that you can reason about because you can focus on the what and not the how of model construction.
+
+The other noteworthy aspect is that the factories compose together. When we call `MovieFactory()`, `factory_boy` is missing data about everything so it must build all of that data. The challenge is that the `MovieFactory` doesn't know how to build a `Director` or any of the movie's foreign key relationships. Instead, the factory will delegate to *other* factories using the `SubFactory` attribute. By delegating to other factories, `factory_boy` can build the model and its entire tree of relationships with a single call.
+
+When we want to override the behavior of some of the generated data, we pass in the extra argument as I did in the second example by providing "Sci-Fi" as the `genre`. You can pass in other model instances to your factories too.
+
+`factory_boy` makes testing with database records a joy. In my experience, most of my Django tests require some amount of database data so I use factories very heavily. I think you will find that `factory_boy` is a worthy addition to your test tools.
## Summary
@@ -1093,13 +456,10 @@ In this article,
{{< book >}}
In this chapter,
{{< /book >}}
-we explored tests
-with Django projects.
-We focused on:
+we explored tests with Django projects. We focused on:
* Why anyone would want to write automated tests
-* What kinds of tests are useful
- to a Django app
+* What kinds of tests are useful to a Django app
* What tools you can use to make testing easier
{{< web >}}
@@ -1108,35 +468,14 @@ Next time,
{{< book >}}
In the next chapter,
{{< /book >}}
-we will dig into deployment.
-Deployment is getting your project
-into the environment
-where you will share your application
-for use.
-This might be the internet
-or it might be a private network
-for your company.
-Wherever you're putting your app,
-you'll want to know about:
-
-* Deploying your application with a Python web application server
- (i.e., `./manage.py runserver` isn't meant for deployed apps)
-* Deployment preconditions
- for managing settings, migrations, and static files
-* A checklist to confirm that your settings are configured
- with the proper security guards
+we will dig into deployment. Deployment is getting your project into the environment where you will share your application for use. This might be the internet or it might be a private network for your company. Wherever you're putting your app, you'll want to know about:
+
+* Deploying your application with a Python web application server (i.e., `./manage.py runserver` isn't meant for deployed apps)
+* Deployment preconditions for managing settings, migrations, and static files
+* A checklist to confirm that your settings are configured with the proper security guards
* Monitoring your application for errors
{{< web >}}
-If you'd like to follow along
-with the series,
-please feel free to sign up
-for my newsletter
-where I announce all of my new content.
-If you have other questions,
-you can reach me online
-on Twitter
-where I am
-{{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
+If you'd like to follow along with the series, please feel free to sign up for my newsletter where I announce all of my new content. If you have other questions, you can reach me online on Twitter where I am {{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
{{< /web >}}
From c060ee2e64b256860090c5a5581fa4cc9407adcb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nazar=C3=A9=20da=20Piedade?=
Date: Fri, 5 May 2023 11:18:31 +0100
Subject: [PATCH 14/30] chore: format
`understand-django/2021-03-23-deploy-site-live.pt.md`
---
.../2021-03-23-deploy-site-live.pt.md | 680 +++---------------
1 file changed, 115 insertions(+), 565 deletions(-)
diff --git a/content/understand-django/2021-03-23-deploy-site-live.pt.md b/content/understand-django/2021-03-23-deploy-site-live.pt.md
index 31d64bbd..6f9c581b 100644
--- a/content/understand-django/2021-03-23-deploy-site-live.pt.md
+++ b/content/understand-django/2021-03-23-deploy-site-live.pt.md
@@ -1,13 +1,7 @@
---
title: "Deploy A Site Live"
description: >-
- You're ready to take the site you developed
- and share it with the world.
- What steps should you take
- to prepare your Django project
- for life on the web?
- That's the focus
- of this article.
+ You're ready to take the site you developed and share it with the world. What steps should you take to prepare your Django project for life on the web? That's the focus of this article.
image: img/django.png
type: post
categories:
@@ -23,180 +17,64 @@ series: "Understand Django"
In the previous
{{< web >}}
-[Understand Django]({{< ref "/understand-django/_index.md" >}})
-article,
+[Understand Django]({{< ref "/understand-django/_index.pt.md" >}}) article,
{{< /web >}}
{{< book >}}
chapter,
{{< /book >}}
-we looked at automated testing
-and how writing tests
-to check your Django project
-can be very valuable,
-saving you time
-and making sure your site works
-for your users.
-Next,
-we're going to look into
-how to share your site
-on the internet
-by understanding what it means
-to *deploy* a Django project.
-Deployment is the act
-of making your application live
-to your audience,
+we looked at automated testing and how writing tests to check your Django project can be very valuable, saving you time and making sure your site works for your users. Next, we're going to look into how to share your site on the internet by understanding what it means to *deploy* a Django project. Deployment is the act of making your application live to your audience,
{{< web >}}
and this article explains the actions
{{< /web >}}
{{< book >}}
and this chapter explains the actions
{{< /book >}}
-you should consider
-to deploy effectively.
+you should consider to deploy effectively.
-{{< understand-django-series "deployment" >}}
+{{< understand-django-series-pt "deployment" >}}
## Pick A Python Application Server
-When you begin to learn Django,
-the documentation will instruct you
-to use `./manage.py runserver`
-to interact with your application locally.
-`runserver` is a great tool
-for getting started
-because you can avoid extra software
-from the outset
-of your Django journey.
-
-While great,
-the `runserver` command is not designed
-for handling a lot of web traffic.
-`runserver` is explicitly intended
-for a development-only setting.
-Aside from a lack of performance tuning options,
-the server doesn't receive the same security scrutiny
-as other Python web application servers.
-
-These factors add up
-to make the `runserver` command unsuitable
-for handling your live site.
-What should you use instead?
-When you read the {{< extlink "https://docs.djangoproject.com/en/4.1/howto/deployment/wsgi/" "deployment documentation" >}},
-you'll find many possible Python web application servers listed.
-Gunicorn, uWSGI, Apache with mod_wsgi, Daphne, Hypercorn, and Uvicorn
-are all presented as available options.
-That's way too much choice.
+When you begin to learn Django, the documentation will instruct you to use `./manage.py runserver` to interact with your application locally. `runserver` is a great tool for getting started because you can avoid extra software from the outset of your Django journey.
+
+While great, the `runserver` command is not designed for handling a lot of web traffic. `runserver` is explicitly intended for a development-only setting. Aside from a lack of performance tuning options, the server doesn't receive the same security scrutiny as other Python web application servers.
+
+These factors add up to make the `runserver` command unsuitable for handling your live site. What should you use instead? When you read the {{< extlink "https://docs.djangoproject.com/en/4.1/howto/deployment/wsgi/" "deployment documentation" >}}, you'll find many possible Python web application servers listed. Gunicorn, uWSGI, Apache with mod_wsgi, Daphne, Hypercorn, and Uvicorn are all presented as available options. That's way too much choice.
> Use {{< extlink "https://gunicorn.org/" "Gunicorn" >}}.
-Gunicorn (which stands for "Green Unicorn") is a very simple web application server
-to start using.
-In my experience,
-Gunicorn *stays* easy to use
-and works for projects that receive a ton of traffic.
-I've used some of the other options presented
-for Django apps,
-and few are as simple
-to use as Gunicorn.
-
-To use Gunicorn,
-we need to point the `gunicorn` command
-to the WSGI application
-that Django projects have.
-If you recall,
-WSGI is the Web Server Gateway Interface.
-WSGI is the protocol that permits Django apps
-to talk to any of these web application servers.
-
-If you ran the `startproject` command
-and named your Django project as "project,"
-the WSGI application should be in a file
-like `project/wsgi.py`.
-Gunicorn is aware that Django conventionally calls
-the WSGI application in that module `application`,
-so your only action is to point Gunicorn
-to the module
-with Python's dotted syntax.
-Here's the most basic setup.
+Gunicorn (which stands for "Green Unicorn") is a very simple web application server to start using. In my experience, Gunicorn *stays* easy to use and works for projects that receive a ton of traffic. I've used some of the other options presented for Django apps, and few are as simple to use as Gunicorn.
+
+To use Gunicorn, we need to point the `gunicorn` command to the WSGI application that Django projects have. If you recall, WSGI is the Web Server Gateway Interface. WSGI is the protocol that permits Django apps to talk to any of these web application servers.
+
+If you ran the `startproject` command and named your Django project as "project," the WSGI application should be in a file like `project/wsgi.py`. Gunicorn is aware that Django conventionally calls the WSGI application in that module `application`, so your only action is to point Gunicorn to the module with Python's dotted syntax. Here's the most basic setup:
```bash
$ gunicorn project.wsgi
```
-Gunicorn works by starting a main process
-that will listen
-for HTTP requests
-on the local machine
-at port 5000
-by default.
-As each request reaches the main process,
-the request routes to an available worker process.
-The worker process executes your Django app code
-to provide the response data
-to the user.
-
-By default,
-Gunicorn will only create a single worker process.
-The Gunicorn documentation recommends picking a number
-that is two to four times larger
-than the number
-of CPU cores available
-to your machine.
-
-The number of workers is a large determining factor
-in how many requests
-your Django app can handle
-at once.
-The number of requests processed is usually called *traffic*
-by web developers.
-The idea of handling more traffic
-by creating more processes (i.e., Gunicorn workers)
-is called *horizontal scaling*.
-In contrast,
-*vertical scaling* handles more traffic
-by using a better individual computer.
-A faster processor with a single CPU can handle more requests.
-When thinking about performance,
-horizontal scaling is often a far easier approach.
-
-One of my projects
-has a small amount of traffic
-and runs on a single CPU
-on its hosting provider.
-In that scenario,
-I use two workers
-which looks like:
+Gunicorn works by starting a main process that will listen for HTTP requests on the local machine at port 5000 by default. As each request reaches the main process, the request routes to an available worker process. The worker process executes your Django app code to provide the response data to the user.
+
+By default, Gunicorn will only create a single worker process. The Gunicorn documentation recommends picking a number that is two to four times larger than the number of CPU cores available to your machine.
+
+The number of workers is a large determining factor in how many requests your Django app can handle at once. The number of requests processed is usually called *traffic* by web developers. The idea of handling more traffic by creating more processes (i.e., Gunicorn workers) is called *horizontal scaling*. In contrast, *vertical scaling* handles more traffic by using a better individual computer. A faster processor with a single CPU can handle more requests. When thinking about performance, horizontal scaling is often a far easier approach.
+
+One of my projects has a small amount of traffic and runs on a single CPU on its hosting provider. In that scenario, I use two workers which looks like:
```bash
$ gunicorn project.wsgi --workers 2
```
-The only other option you may require is an option
-to handle where logging data goes.
-I haven't covered logging in depth yet,
+The only other option you may require is an option to handle where logging data goes. I haven't covered logging in depth yet,
{{< web >}}
but recall from previous articles
{{< /web >}}
{{< book >}}
but recall from previous chapters
{{< /book >}}
-that logging allows you to record information
-about what your application is doing
-while it's running.
-
-Some hosting providers expect monitoring output
-like logging
-to go to stdout or stderr.
-stdout stands for "standard output"
-and stderr is "standard error."
-stdout is where data appears
-in your terminal
-when you use `print`.
-To tell Gunicorn to log
-to stderr,
-you can use a dash as the value
-to the `log-file` option.
-My full Gunicorn command looks like:
+that logging allows you to record information about what your application is doing while it's running.
+
+Some hosting providers expect monitoring output like logging to go to stdout or stderr. stdout stands for "standard output" and stderr is "standard error." stdout is where data appears in your terminal when you use `print`. To tell Gunicorn to log to stderr, you can use a dash as the value to the `log-file` option. My full Gunicorn command looks like:
```bash
$ gunicorn project.wsgi \
@@ -204,46 +82,15 @@ $ gunicorn project.wsgi \
--log-file -
```
-A note about ASGI:
-I am assuming that your use of Django will use WSGI
-and its synchronous mode.
-In recent years,
-Django added support for asynchronous Python.
-Asynchronous Python brings the promise
-of higher performance
-with the tradeoff of some implementation complexity.
-For learning Django initially,
-you don't need to understand asynchronous Python
-and the Asynchronous Server Gateway Interface (ASGI).
+A note about ASGI: I am assuming that your use of Django will use WSGI and its synchronous mode. In recent years, Django added support for asynchronous Python. Asynchronous Python brings the promise of higher performance with the tradeoff of some implementation complexity. For learning Django initially, you don't need to understand asynchronous Python and the Asynchronous Server Gateway Interface (ASGI).
## Pick Your Cloud
-Once you know which application server to use
-and how to use it,
-you need to run your code somewhere.
-Again,
-you can be paralyzed
-by the sheer volume
-of choices available to you.
-AWS, GCP, Azure, Digital Ocean, Linode, Heroku, PythonAnywhere,
-and so many other cloud vendors are out there
-and able to run your application.
-
-If you're getting started,
-use a Platform as a Service (PaaS) option.
-Specifically,
-I think Heroku is a great platform
-for applications.
-A PaaS removes loads
-of operational complexity
-that you may be unequipped
-to handle initially
-if you're newer to web development.
-
-Think you want to run your application
-on a general cloud provider like AWS?
-You can certainly do that,
-but you'll potentially need to be prepared for:
+Once you know which application server to use and how to use it, you need to run your code somewhere. Again, you can be paralyzed by the sheer volume of choices available to you. AWS, GCP, Azure, Digital Ocean, Linode, Heroku, PythonAnywhere, and so many other cloud vendors are out there and able to run your application.
+
+If you're getting started, use a Platform as a Service (PaaS) option. Specifically, I think Heroku is a great platform for applications. A PaaS removes loads of operational complexity that you may be unequipped to handle initially if you're newer to web development.
+
+Think you want to run your application on a general cloud provider like AWS? You can certainly do that, but you'll potentially need to be prepared for:
* Setting up machines
* Getting TLS certificates for https
@@ -251,77 +98,30 @@ but you'll potentially need to be prepared for:
* Using configuration management tools to automate deployment
* And loads more!
-You may be using Django to learn those skills.
-If so,
-that's awesome and good luck!
-But if your primary goal is to get your application out
-into the world,
-then these tasks are a huge drag
-on your productivity.
-
-Let's contrast this
-with Heroku.
-Because Heroku is a PaaS,
-they deal with the vast majority
-of the setup and coordination
-of machines in their cloud.
-Your experience as a developer primarily moves
-to a single command:
+You may be using Django to learn those skills. If so, that's awesome and good luck! But if your primary goal is to get your application out into the world, then these tasks are a huge drag on your productivity.
+
+Let's contrast this with Heroku. Because Heroku is a PaaS, they deal with the vast majority of the setup and coordination of machines in their cloud. Your experience as a developer primarily moves to a single command:
```bash
$ git push heroku main
```
-The Heroku instructions have you set up a Git remote.
-That remote is the place you push your code to
-and let Heroku handle the deployment
-of your application.
-Heroku manages this by cleverly detecting Django applications
-and applying the correct commands.
-We'll see some of those required commands
-when looking at the preconditions.
-
-To be really clear,
-this is not an ad for Heroku.
-I have personal experience with most of the cloud vendors
-that I listed earlier
-in this section.
-I have found that a PaaS like Heroku is far and away an easier option
-to apply for my own projects.
-That's why I recommend the service so strongly.
+The Heroku instructions have you set up a Git remote. That remote is the place you push your code to and let Heroku handle the deployment of your application. Heroku manages this by cleverly detecting Django applications and applying the correct commands. We'll see some of those required commands when looking at the preconditions.
+
+To be really clear, this is not an ad for Heroku. I have personal experience with most of the cloud vendors that I listed earlier in this section. I have found that a PaaS like Heroku is far and away an easier option to apply for my own projects. That's why I recommend the service so strongly.
## Project Preconditions
-Django has a few preconditions
-that it expects
-before running your application
-in a live setting.
+Django has a few preconditions that it expects before running your application in a live setting.
{{< web >}}
-If you've read the previous articles,
-then you've actually seen most
-of these preconditions by now,
+If you've read the previous articles, then you've actually seen most of these preconditions by now,
{{< /web >}}
{{< book >}}
-You've seen most
-of these preconditions by now
-in earlier chapters,
+You've seen most of these preconditions by now in earlier chapters,
{{< /book >}}
-but we'll group them together in this section
-so you can see the complete picture.
-
-One that we haven't discussed
-is the `DJANGO_SETTINGS_MODULE` environment variable.
-This is one critical element
-to your application
-because the variable signals
-to your Django application
-where the settings module is located.
-If you have different settings modules
-for different configurations
-(e.g., a live site configuration versus a unit testing configuration),
-then you may need to specify
-which settings module
-Django should use when running.
+but we'll group them together in this section so you can see the complete picture.
+
+One that we haven't discussed is the `DJANGO_SETTINGS_MODULE` environment variable. This is one critical element to your application because the variable signals to your Django application where the settings module is located. If you have different settings modules for different configurations (e.g., a live site configuration versus a unit testing configuration), then you may need to specify which settings module Django should use when running.
{{< web >}}
In a future article,
@@ -329,139 +129,64 @@ In a future article,
{{< book >}}
In a future chapter,
{{< /book >}}
-we'll focus on how to manage your settings modules.
-At that time,
-you'll see how using some particular techniques diminish the need
-for multiple modules.
-For now,
-keep in mind `DJANGO_SETTINGS_MODULE` for deployments,
-and you should be good.
-
-The next important precondition
-for your app is keeping your database
-in sync using migrations.
-As mentioned
+we'll focus on how to manage your settings modules. At that time, you'll see how using some particular techniques diminish the need for multiple modules. For now, keep in mind `DJANGO_SETTINGS_MODULE` for deployments, and you should be good.
+
+The next important precondition for your app is keeping your database in sync using migrations. As mentioned
{{< web >}}
in the models article,
{{< /web >}}
{{< book >}}
in the models chapter,
{{< /book >}}
-we make migrations
-when making model changes.
-These migrations generate instructions
-for your relational database,
-so that Django can match the database schema.
-
-Without the migration system,
-Django would be unable to communicate effectively
-with the database.
-Because of that,
-you need to ensure
-that you have applied all migrations
-to your application
-before running your app.
-
-For whatever cloud you're using,
-you need to make sure
-that when you deploy,
-your deployment scripts run:
+we make migrations when making model changes. These migrations generate instructions for your relational database, so that Django can match the database schema.
+
+Without the migration system, Django would be unable to communicate effectively with the database. Because of that, you need to ensure that you have applied all migrations to your application before running your app.
+
+For whatever cloud you're using, you need to make sure that when you deploy, your deployment scripts run:
```bash
$ ./manage.py migrate
```
-For instance,
-with my Heroku setup,
-Heroku lets me define a "release" command
-that they guarantee to run
-before launching the new version
-of the app.
-Heroku uses a `Procfile` to set
-which machines and commands to run
-so my `Procfile` looks like:
+For instance, with my Heroku setup, Heroku lets me define a "release" command that they guarantee to run before launching the new version of the app. Heroku uses a `Procfile` to set which machines and commands to run so my `Procfile` looks like:
```yaml
release: python manage.py migrate
web: gunicorn project.wsgi --workers 2 --log-file -
```
-This file tells Heroku
-to run migrations before launching,
-then run gunicorn
-as the web process
-for the application.
+This file tells Heroku to run migrations before launching, then run gunicorn as the web process for the application.
-Another precondition needed
-for your app
-is static files.
+Another precondition needed for your app is static files.
{{< web >}}
We saw in the static files article
{{< /web >}}
{{< book >}}
We saw in the static files chapter
{{< /book >}}
-that Django looks
-for static files
-in a single directory
-for performance reasons.
-That requires running a command
-to put those files
-in the expected location.
+that Django looks for static files in a single directory for performance reasons. That requires running a command to put those files in the expected location:
```bash
$ ./manage.py collectstatic
```
-In my deployment process
-for Heroku,
-this is a step that Heroku automatically does
-because it can detect a Django project.
+In my deployment process for Heroku, this is a step that Heroku automatically does because it can detect a Django project.
-These items are the required preconditions
-to run your application.
-Django also has steps
-that aren't strictly required
-to make your app work,
-but are very beneficial.
-Let's look at how to address those next.
+These items are the required preconditions to run your application. Django also has steps that aren't strictly required to make your app work, but are very beneficial. Let's look at how to address those next.
## Protecting Your Site
-"Put on your seat belt."
-The average person knows
-that it's wise
-to wear a seat belt
-in a car.
-The statistical data is overwhelming
-that a seat belt can help save your life
-if you're ever
-in a car accident.
-Yet,
-a seat belt is not strictly necessary
-(aside from a legal perspective)
-to operate a vehicle.
-
-Django includes a command
-that produces a set of instructive safety messages
-for important site settings and configurations.
-Thankfully,
-ignoring these messages is unlikely
-to affect your personal health,
-but the messages are valuable
-to help you combat the bad forces
-that exist
-on the public internet.
-
-To view these important messages,
-run:
+"Put on your seat belt." The average person knows that it's wise to wear a seat belt in a car. The statistical data is overwhelming that a seat belt can help save your life if you're ever in a car accident. Yet, a seat belt is not strictly necessary (aside from a legal perspective) to operate a vehicle.
+
+Django includes a command that produces a set of instructive safety messages for important site settings and configurations. Thankfully, ignoring these messages is unlikely to affect your personal health, but the messages are valuable to help you combat the bad forces that exist on the public internet.
+
+To view these important messages, run:
```bash
$ ./manage.py check --deploy --fail-level WARNING
```
-On a little sample project
-that I created,
+On a little sample project that I created,
{{< web >}}
the (slightly reformatted for the article) output looks like:
{{< /web >}}
@@ -496,61 +221,22 @@ WARNINGS:
System check identified 6 issues (0 silenced).
```
-The items reported by the checklist are often about settings
-that could be configured better.
-These checks are created
-by the {{< extlink "https://docs.djangoproject.com/en/4.1/topics/checks/" "System check framework" >}}
-that comes with Django.
-
-You should review each of the checks
-and learn about the changes
-that the check recommends.
-The items that appear with the `--deploy` flag are usually quite important
-and fixing them can greatly improve the safety
-and security
-of your application.
-
-Some of these checks are too modest about their importance.
-For instance, `security.W018` is the warning
-that tells you that `DEBUG` is set to `True`
-in the settings.
-`DEBUG = True` is *TERRIBLE*
-for a live site
-since it can *trivially* leak loads of private data.
-
-As a *warning*,
-`security.W018` will *not* fail the deploy check
-because `./manage.py check` defaults
-to failing
-on things that are errors.
-If you want to make sure
-that your site is sufficiently protected,
-I strongly encourage you
-to add the `--fail-level WARNING` flag
-so that the check will give those warnings the weight
-that they likely deserve.
-
-What do you do if the check is handled
-by some other part
-of your system?
-For example,
-maybe you've set up a secure configuration
-with HTTPS,
-and you've set HSTS headers
-with a reverse proxy
-like Nginx
-(this was one of the configurations
-that I mentioned
+The items reported by the checklist are often about settings that could be configured better. These checks are created by the {{< extlink "https://docs.djangoproject.com/en/4.1/topics/checks/" "System check framework" >}} that comes with Django.
+
+You should review each of the checks and learn about the changes that the check recommends. The items that appear with the `--deploy` flag are usually quite important and fixing them can greatly improve the safety and security of your application.
+
+Some of these checks are too modest about their importance. For instance, `security.W018` is the warning that tells you that `DEBUG` is set to `True` in the settings. `DEBUG = True` is *TERRIBLE* for a live site since it can *trivially* leak loads of private data.
+
+As a *warning*, `security.W018` will *not* fail the deploy check because `./manage.py check` defaults to failing on things that are errors. If you want to make sure that your site is sufficiently protected, I strongly encourage you to add the `--fail-level WARNING` flag so that the check will give those warnings the weight that they likely deserve.
+
+What do you do if the check is handled by some other part of your system? For example, maybe you've set up a secure configuration with HTTPS, and you've set HSTS headers with a reverse proxy like Nginx (this was one of the configurations that I mentioned
{{< web >}}
in the static files article).
{{< /web >}}
{{< book >}}
in the static files chapter).
{{< /book >}}
-If HSTS is handled elsewhere,
-you could set the `SILENCED_SYSTEM_CHECKS` setting
-to tell Django
-that you took care of it.
+If HSTS is handled elsewhere, you could set the `SILENCED_SYSTEM_CHECKS` setting to tell Django that you took care of it:
```python
# project/settings.py
@@ -560,111 +246,42 @@ SILENCED_SYSTEM_CHECKS = [
]
```
-Once you have finished the checklist,
-your application will be much better equipped
-to handle the hostile internet,
-but things can still go wrong.
-What should you do about errors
-that happen on your live site?
-Let's look at that next.
+Once you have finished the checklist, your application will be much better equipped to handle the hostile internet, but things can still go wrong. What should you do about errors that happen on your live site? Let's look at that next.
## Prepare For Errors
-If an error happens on a live site
-and the site administrator (i.e., *you*) didn't hear it,
-did it really happen?
-**Yes, yes it did.**
-
-Dealing with a live site brings a new set
-of challenges.
-Try as we might to consider every possible action
-that our users do,
-we'll never get them all.
-There are lots
-of ways
-that a site can have errors
-from things that we failed
-to consider.
-Since errors *will* happen
-with a large enough product
-and large enough customer base,
-we need some plan to manage them.
+If an error happens on a live site and the site administrator (i.e., *you*) didn't hear it, did it really happen? **Yes, yes it did.**
+
+Dealing with a live site brings a new set of challenges. Try as we might to consider every possible action that our users do, we'll never get them all. There are lots of ways that a site can have errors from things that we failed to consider. Since errors *will* happen with a large enough product and large enough customer base, we need some plan to manage them.
We can consider a few strategies:
#### 1. Do nothing.
-While I don't recommend this strategy,
-you *could* wait for your customers
-to report errors to you.
-Some portion of customers might actually write to you
-and report a problem,
-but the vast majority won't.
-What's worse is that some
-of these customers may abandon your product
-if the errors are bad enough or frequent enough.
+While I don't recommend this strategy, you *could* wait for your customers to report errors to you. Some portion of customers might actually write to you and report a problem, but the vast majority won't. What's worse is that some of these customers may abandon your product if the errors are bad enough or frequent enough.
> Using your customers to learn about errors makes for a poor user experience.
#### 2. Use error emails.
-The Django deployment documentation highlights Django's ability
-to send error information
-to certain email addresses.
-*I don't recommend this strategy either.*
-Why?
-
-* Setting up email properly can be a very tricky endeavor
- that involves far more configuration
- than you may realize.
- You may need email for your service,
- but setting it up for error info alone is overkill.
-* The error emails can include Python tracebacks to provide context,
- but other tools can provide much richer context information
- (e.g., the browser used when a customer experiences an error).
-* If you have a runaway error
- that happens constantly on your site,
- you can say "bye, bye"
- to your email inbox.
- A flood of emails is a quick way to get email accounts flagged
- and hurt the deliverability of email.
-
-This brings us to the final strategy
-that I'll cover.
+The Django deployment documentation highlights Django's ability to send error information to certain email addresses. *I don't recommend this strategy either.* Why?
+
+* Setting up email properly can be a very tricky endeavor that involves far more configuration than you may realize. You may need email for your service, but setting it up for error info alone is overkill.
+* The error emails can include Python tracebacks to provide context, but other tools can provide much richer context information (e.g., the browser used when a customer experiences an error).
+* If you have a runaway error that happens constantly on your site, you can say "bye, bye" to your email inbox. A flood of emails is a quick way to get email accounts flagged and hurt the deliverability of email.
+
+This brings us to the final strategy that I'll cover.
#### 3. Use an error tracking service.
-Error tracking services are specifically designed
-to collect context about errors,
-aggregate common errors together,
-and generally give you tools
-to respond to your site's errors appropriately.
-
-**I find that error tracking services are the best tools
-to understand what's going wrong
-on your live site**
-because the tools are purpose-built
-for detailing error behavior.
-
-Many of these services are not complicated
-to get installed and configured,
-and the services often have an extremely generous free tier
-to monitor your application.
-
-In the Django world,
-I generally hear about two
-of these error tracking services:
-{{< extlink "https://rollbar.com/" "Rollbar" >}}
-and {{< extlink "https://sentry.io/welcome/" "Sentry" >}}.
-I've used both of these error trackers,
-and I think they are both great.
-For my personal projects,
-I happen to pick Rollbar
-by default,
-so I'll describe
-that service
-in this section
-as an example.
+Error tracking services are specifically designed to collect context about errors, aggregate common errors together, and generally give you tools to respond to your site's errors appropriately.
+
+**I find that error tracking services are the best tools to understand what's going wrong on your live site** because the tools are purpose-built for detailing error behavior.
+
+Many of these services are not complicated to get installed and configured, and the services often have an extremely generous free tier to monitor your application.
+
+In the Django world, I generally hear about two of these error tracking services: {{< extlink "https://rollbar.com/" "Rollbar" >}}
+and {{< extlink "https://sentry.io/welcome/" "Sentry" >}}. I've used both of these error trackers, and I think they are both great. For my personal projects, I happen to pick Rollbar by default, so I'll describe that service in this section as an example.
The flow for installing Rollbar is:
@@ -674,8 +291,7 @@ The flow for installing Rollbar is:
That's it!
-My Rollbar configuration
-for one of my projects looks like:
+My Rollbar configuration for one of my projects looks like:
```python
# project/settings.py
@@ -689,42 +305,17 @@ ROLLBAR = {
}
```
-In this example,
-everything coming from `env` is from an environment variable
-that we'll discuss more when we focus on settings management
-in Django.
-
-The `enabled` parameter can quickly turn Rollbar on and off.
-This is good for local development
-so you're not sending data to the service when working on new features.
-
-The `access_token` is the secret that Rollbar will provide
-in your account
-to associate your app's error data
-with your account
-so you can see problems.
-
-The `environment` setting lets Rollbar split your errors
-into different groupings.
-You can use this to separate different configurations
-that you put on the internet.
-For instance,
-the software industry likes to call live sites "production."
-You may also have a separate site
-that is available privately
-to a team
-that you might call "development."
-
-The other settings tell Rollbar information
-that can help map errors back to your code repository.
-
-Once you set this up,
-how can you tell that it's working?
-Like a musician tapping a microphone
-to see if it's working,
-I like to add a view to my code
-that lets me test
-that my error tracking service is operational.
+In this example, everything coming from `env` is from an environment variable that we'll discuss more when we focus on settings management in Django.
+
+The `enabled` parameter can quickly turn Rollbar on and off. This is good for local development so you're not sending data to the service when working on new features.
+
+The `access_token` is the secret that Rollbar will provide in your account to associate your app's error data with your account so you can see problems.
+
+The `environment` setting lets Rollbar split your errors into different groupings. You can use this to separate different configurations that you put on the internet. For instance, the software industry likes to call live sites "production." You may also have a separate site that is available privately to a team that you might call "development."
+
+The other settings tell Rollbar information that can help map errors back to your code repository.
+
+Once you set this up, how can you tell that it's working? Like a musician tapping a microphone to see if it's working, I like to add a view to my code that lets me test that my error tracking service is operational:
```python
# application/views.py
@@ -737,27 +328,9 @@ def boom(request):
raise Exception("Is this thing on?")
```
-I connect this view to a URL configuration,
-then check it after I deploy Rollbar
-for the first time.
-Importantly,
-don't forget to include a `staff_member_required` decorator
-so that random people
-on the internet can't trigger errors
-on your server on a whim!
-
-With error tracking set up,
-you'll be in a good position
-to see errors when they happen
-on your site.
-In fact,
-a great way to win the favor
-of customers can be to fix errors proactively
-and reach out to them.
-Most people don't enjoy contacting support
-and would be surprised and delighted
-if you tell them
-that you fixed their problem immediately.
+I connect this view to a URL configuration, then check it after I deploy Rollbar for the first time. Importantly, don't forget to include a `staff_member_required` decorator so that random people on the internet can't trigger errors on your server on a whim!
+
+With error tracking set up, you'll be in a good position to see errors when they happen on your site. In fact, a great way to win the favor of customers can be to fix errors proactively and reach out to them. Most people don't enjoy contacting support and would be surprised and delighted if you tell them that you fixed their problem immediately.
## Summary
@@ -767,18 +340,12 @@ In this article,
{{< book >}}
In this chapter,
{{< /book >}}
-we learned the things
-to consider when deploying a site
-to the internet.
-We examined:
+we learned the things to consider when deploying a site to the internet. We examined:
-* Deploying your application with a Python web application server
- (i.e., `./manage.py runserver` isn't meant for deployed apps)
+* Deploying your application with a Python web application server (i.e., `./manage.py runserver` isn't meant for deployed apps)
* Running your app on a cloud vendor
-* Deployment preconditions
- for managing settings, migrations, and static files
-* A checklist to confirm that your settings are configured
- with the proper security guards
+* Deployment preconditions for managing settings, migrations, and static files
+* A checklist to confirm that your settings are configured with the proper security guards
* Monitoring your application for errors
{{< web >}}
@@ -787,30 +354,13 @@ In the next article,
{{< book >}}
In the next chapter,
{{< /book >}}
-we'll look at Django's tools
-for managing shorter term user data
-like authentication info
-with Django sessions.
-We'll see the different modes
-that Django provides
-and how to use sessions
-to support your project.
-You'll learn about:
+we'll look at Django's tools for managing shorter term user data like authentication info with Django sessions. We'll see the different modes that Django provides and how to use sessions to support your project. You'll learn about:
* What sessions are and how they work
* Ways that Django uses sessions
* How to use sessions in your apps
{{< web >}}
-If you'd like to follow along
-with the series,
-please feel free to sign up
-for my newsletter
-where I announce all of my new content.
-If you have other questions,
-you can reach me online
-on Twitter
-where I am
-{{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
+If you'd like to follow along with the series, please feel free to sign up for my newsletter where I announce all of my new content. If you have other questions, you can reach me online on Twitter where I am {{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
{{< /web >}}
From e21fd639243764f2b32cab1e7a37e33308513d2a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nazar=C3=A9=20da=20Piedade?=
Date: Fri, 5 May 2023 11:18:57 +0100
Subject: [PATCH 15/30] chore: format
`understand-django/2021-05-21-sessions.pt.md`
---
.../2021-05-21-sessions.pt.md | 442 ++++--------------
1 file changed, 82 insertions(+), 360 deletions(-)
diff --git a/content/understand-django/2021-05-21-sessions.pt.md b/content/understand-django/2021-05-21-sessions.pt.md
index 01beba6f..50deca23 100644
--- a/content/understand-django/2021-05-21-sessions.pt.md
+++ b/content/understand-django/2021-05-21-sessions.pt.md
@@ -1,15 +1,7 @@
---
title: "Per-visitor Data With Sessions"
description: >-
- How does Django know
- when a user is logged in?
- Where can the framework store data
- for a visitor on your app?
- In this article,
- we'll answer those questions
- and look at a storage concept
- in Django
- called sessions.
+ How does Django know when a user is logged in? Where can the framework store data for a visitor on your app? In this article, we'll answer those questions and look at a storage concept in Django called sessions.
image: img/django.png
type: post
categories:
@@ -25,54 +17,24 @@ series: "Understand Django"
In the last
{{< web >}}
-[Understand Django]({{< ref "/understand-django/_index.md" >}})
-article,
+[Understand Django]({{< ref "/understand-django/_index.pt.md" >}}) article,
{{< /web >}}
{{< book >}}
chapter,
{{< /book >}}
-we saw
-what it takes
-to make your Django project live
-on the internet.
-Now,
-we'll get back to a more narrow topic
-and focus on a way Django can store data
-for visitors to your site.
-This is the kind of data
-that doesn't often fit well
-into your Django models
-and is called *session* data.
-
-{{< understand-django-series "sessions" >}}
+we saw what it takes to make your Django project live on the internet. Now, we'll get back to a more narrow topic and focus on a way Django can store data for visitors to your site. This is the kind of data that doesn't often fit well into your Django models and is called *session* data.
+
+{{< understand-django-series-pt "sessions" >}}
## What Is A Session?
-As *I* was learning Django,
-I'd run into sessions occasionally
-and accept that I didn't really understand them.
-They felt like magic to me.
-But what is a session?
-
-> A session is a set of data
-that is available to users
-that Django can use over multiple requests.
-
-From a development perspective,
-the data is different
-from the regular data
-that you would store in a database
-with Django models.
-When working with session data,
-you don't query the database
-using the ORM.
-Instead,
-you can access session content
-via the `request.session` attribute.
-
-The `request.session` is a dictionary-like object.
-Storing data into the session is like working
-with any other Python dictionary.
+As *I* was learning Django, I'd run into sessions occasionally and accept that I didn't really understand them. They felt like magic to me. But what is a session?
+
+> A session is a set of data that is available to users that Django can use over multiple requests.
+
+From a development perspective, the data is different from the regular data that you would store in a database with Django models. When working with session data, you don't query the database using the ORM. Instead, you can access session content via the `request.session` attribute.
+
+The `request.session` is a dictionary-like object. Storing data into the session is like working with any other Python dictionary:
```python
# application/view.py
@@ -84,48 +46,22 @@ def a_session_view(request):
return HttpReponse('')
```
-When Django stores the session data,
-the framework will keep the data
-in a JSON format.
-What is JSON?
-I've mentioned JSON in passing
+When Django stores the session data, the framework will keep the data in a JSON format. What is JSON? I've mentioned JSON in passing
{{< web >}}
in previous articles,
{{< /web >}}
{{< book >}}
in previous chapters,
{{< /book >}}
-but now is a decent time to explain it.
-Knowing what JSON is will help you understand
-what happens to session data
-as the data is stored.
+but now is a decent time to explain it. Knowing what JSON is will help you understand what happens to session data as the data is stored.
### The "What is JSON?" Sidebar
-JSON is a data format.
-JSON is a way of describing data
-so that the data can be stored
-or transmitted.
-The definition of that format is listed
-on the official {{< extlink "https://www.json.org/json-en.html" "JSON website" >}}
-and can be understood
-in probably 10 minutes or less.
-
-That stored data can be parsed
-based on the definition of the format
-to recreate the data
-at a different time
-or on a different computer.
-In general,
-you can view JSON as a tool
-to take Python dictionaries or lists
-and store or transmit them
-for use elsewhere.
-
-The Python standard library includes a module
-for working with JSON data.
-Here's an example to give you an idea
-of what JSON output looks like.
+JSON is a data format. JSON is a way of describing data so that the data can be stored or transmitted. The definition of that format is listed on the official {{< extlink "https://www.json.org/json-en.html" "JSON website" >}} and can be understood in probably 10 minutes or less.
+
+That stored data can be parsed based on the definition of the format to recreate the data at a different time or on a different computer. In general, you can view JSON as a tool to take Python dictionaries or lists and store or transmit them for use elsewhere.
+
+The Python standard library includes a module for working with JSON data. Here's an example to give you an idea of what JSON output looks like:
```python
>>> import json
@@ -140,50 +76,23 @@ of what JSON output looks like.
True
```
-The `dumps` and `loads` functions transform data
-to and from a string, respectively
-(the `s` in the those function names stands for "string").
+The `dumps` and `loads` functions transform data to and from a string, respectively (the `s` in the those function names stands for "string").
-JSON is an extremely versatile format
-and is used all over the internet.
-Getting back to sessions,
-JSON is a good fit
-because there are multiple places
-where Django can store session data.
-Let's look at those next.
+JSON is an extremely versatile format and is used all over the internet. Getting back to sessions, JSON is a good fit because there are multiple places where Django can store session data. Let's look at those next.
## Session Storage
{{< web >}}
-You probably know this drill by now
-if you've been following this series.
+You probably know this drill by now if you've been following this series.
{{< /web >}}
{{< book >}}
You probably know this drill by now.
{{< /book >}}
-Like the template system,
-the ORM,
-and the authentication system,
-the session application is configurable
-with multiple different "engines"
-to store session data.
-
-When you start a new Django project
-with the `startproject` command,
-the session engine will be set
-to `django.contrib.sessions.backends.db`.
-This is because the `SESSION_ENGINE` setting will be unset
-in your settings module,
-and Django will fall back to the default.
-
-With this engine,
-Django will store session data
-in the database.
-Because `startproject` includes the `django.contrib.sessions` app
-in `INSTALLED_APPS`,
-you'd probably see the following stream by
-when you migrate your database
-for the first time.
+Like the template system, the ORM, and the authentication system, the session application is configurable with multiple different "engines" to store session data.
+
+When you start a new Django project with the `startproject` command, the session engine will be set to `django.contrib.sessions.backends.db`. This is because the `SESSION_ENGINE` setting will be unset in your settings module, and Django will fall back to the default.
+
+With this engine, Django will store session data in the database. Because `startproject` includes the `django.contrib.sessions` app in `INSTALLED_APPS`, you'd probably see the following stream by when you migrate your database for the first time:
```text
(venv) $ ./manage.py migrate
@@ -195,244 +104,79 @@ Running migrations:
The `Session` model stores three things:
-* A session key that uniquely identifies the session
- in the storage engine
-* The actual session data,
- stored in JSON format,
- in a `TextField`
+* A session key that uniquely identifies the session in the storage engine
+* The actual session data, stored in JSON format, in a `TextField`
* An expiration date for the session data
-With these three fields,
-Django can handle the temporary storage needs
-for any of your site's visitors.
-
-Why is the session engine configurable?
-Django's session storage is configurable
-to manage tradeoffs.
-The default storage of a database engine
-is a safe default
-and the easiest to understand.
-The answer to "Where is my app's session data?"
-is "In the database with all of my other application data."
-
-If your site grows in popularity
-and usage,
-using the database to store sessions can become a bottleneck
-and limit your performance and application scaling.
-Additionally,
-the default engine creates an ever expanding set
-of database rows in the `Session` model's table.
-You can work around the second challenge
-by periodically running the `clearsessions` management command,
-but what if the performance is a problem for you?
-
-This is where other storage engines might be better
-for your application.
-One method to improve performance is
-to switch to an engine
-that uses caching.
-If you have set up the caching system
-with a technology
-like {{< extlink "https://redis.io/" "Redis" >}}
-or {{< extlink "https://memcached.org/" "Memcached" >}},
-then a lot of session load
-on the database
-can be pushed to the cache service.
-Caching is a topic we will explore more
+With these three fields, Django can handle the temporary storage needs for any of your site's visitors.
+
+Why is the session engine configurable? Django's session storage is configurable to manage tradeoffs. The default storage of a database engine is a safe default and the easiest to understand. The answer to "Where is my app's session data?" is "In the database with all of my other application data."
+
+If your site grows in popularity and usage, using the database to store sessions can become a bottleneck and limit your performance and application scaling. Additionally, the default engine creates an ever expanding set of database rows in the `Session` model's table. You can work around the second challenge by periodically running the `clearsessions` management command, but what if the performance is a problem for you?
+
+This is where other storage engines might be better for your application. One method to improve performance is to switch to an engine that uses caching. If you have set up the caching system with a technology like {{< extlink "https://redis.io/" "Redis" >}} or {{< extlink "https://memcached.org/" "Memcached" >}}, then a lot of session load on the database can be pushed to the cache service. Caching is a topic we will explore more
{{< web >}}
in a future article,
{{< /web >}}
{{< book >}}
in a future chapter,
{{< /book >}}
-so if this doesn't make too much sense right now,
-I apologize for referencing concepts
-that I haven't introduced yet.
-For the time being,
-understand that caching can improve session performance.
-
-Another session storage engine
-that can remove load
-from a database
-uses the browser's cookie system.
-This system will certainly remove database load
-because the state will be stored
-with the browser,
-but this strategy comes
-with its own set
-of tradeoffs.
-With cookie-base storage:
-
-* The storage could be cleared at any time
- by the user.
-* The storage engine is limited to a small amount
- of data storage
- by the browser,
- based on the maximum allowed size
- of a cookie
- (commonly, only 4kB).
-
-Choosing the right session storage engine
-for your application depends
-on what the app does.
-If you're in doubt,
-start with the default of database-backed storage,
-and you should be fine initially.
+so if this doesn't make too much sense right now, I apologize for referencing concepts that I haven't introduced yet. For the time being, understand that caching can improve session performance.
+
+Another session storage engine that can remove load from a database uses the browser's cookie system. This system will certainly remove database load because the state will be stored with the browser, but this strategy comes with its own set of tradeoffs. With cookie-base storage:
+
+* The storage could be cleared at any time by the user.
+* The storage engine is limited to a small amount of data storage by the browser, based on the maximum allowed size of a cookie (commonly, only 4kB).
+
+Choosing the right session storage engine for your application depends on what the app does. If you're in doubt, start with the default of database-backed storage, and you should be fine initially.
## How Does The Session System Identify Visitors?
-When a visitor comes to your site,
-Django needs to associate the session data
-to the visitor.
-To do this association,
-Django will store a session identifier
-in a cookie
-on the user's browser.
-
-On the first visit,
-the session storage engine will look
-for a cookie
-with the name `sessionid` (by default).
-If the application doesn't find that cookie,
-then the session storage will generate a random ID
-and ensure that the random ID doesn't conflict
-with any other session IDs
-that already exist.
-
-From there, the storage engine will store some session data
-via whatever mechanism that engine uses
-(e.g., the database engine will create a new session row
-in the table).
-
-The session ID is added
-to the user's browser cookies
-for your site's domain.
-Cookies are stored in a secured manner
-so only that browser will have access
-to that randomly generated value.
-The session ID is very long (32 characters),
-and the session will expire after a given length of time.
-These characteristics make session IDs quite secure.
-
-Since sessions are secure
-and can uniquely identify a browser,
-what kind of data can we put in there?
+When a visitor comes to your site, Django needs to associate the session data to the visitor. To do this association, Django will store a session identifier in a cookie on the user's browser.
+
+On the first visit, the session storage engine will look for a cookie with the name `sessionid` (by default). If the application doesn't find that cookie, then the session storage will generate a random ID and ensure that the random ID doesn't conflict with any other session IDs that already exist.
+
+From there, the storage engine will store some session data via whatever mechanism that engine uses (e.g., the database engine will create a new session row in the table).
+
+The session ID is added to the user's browser cookies for your site's domain. Cookies are stored in a secured manner so only that browser will have access to that randomly generated value. The session ID is very long (32 characters), and the session will expire after a given length of time. These characteristics make session IDs quite secure.
+
+Since sessions are secure and can uniquely identify a browser, what kind of data can we put in there?
## What Uses Sessions?
-Sessions can store all kinds of data,
-but what are some real world use cases?
-You can look
-in Django's source code
-to find some immediate answers!
-
-In my estimate,
-the most used part of Django
-that uses sessions heavily
-is the auth system.
-We explored the authentication and authorization system
+Sessions can store all kinds of data, but what are some real world use cases? You can look in Django's source code to find some immediate answers!
+
+In my estimate, the most used part of Django that uses sessions heavily is the auth system. We explored the authentication and authorization system
{{< web >}}
-in [User Authentication]({{< ref "/understand-django/2020-11-04-user-authentication.md" >}}).
+in [User Authentication]({{< ref "/understand-django/2020-11-04-user-authentication.pt.md" >}}).
{{< /web >}}
{{< book >}}
in the User Authentication chapter.
{{< /book >}}
-At the time,
-I mentioned
-in the pre-requisites
-that sessions were required,
-but I noted that sessions were an internal detail.
-Now that you know what sessions are about,
-let's see how the auth system uses them.
-
-If you look into the session data
-after you've authenticated,
-you'll find three pieces of information:
+At the time, I mentioned in the pre-requisites that sessions were required, but I noted that sessions were an internal detail. Now that you know what sessions are about, let's see how the auth system uses them.
+
+If you look into the session data after you've authenticated, you'll find three pieces of information:
* The user's ID (stored in `_auth_user_id`)
* The user's hash (stored in `_auth_user_hash`)
-* The string name of the auth backend used
- (stored in `_auth_user_backend`)
-
-Since we know that a session identifies a browser
-and does so securely,
-the auth system stores identity information
-into the session
-to tie that unique session to a unique user.
-When a user's browser makes an HTTP request,
-Django can determine the session associated
-with the request
-via the `sessionid`
-and gain access to the user auth data
-(i.e., the user's ID, hash, and auth backend).
-With these data elements,
-the auth system can determine
-if the request is valid
-and should be considered authenticated
-by checking against the associated auth session data.
-
-The auth system will read which backend is used
-and load that backend if possible.
-The backend is used to load the specific user record
-from the ID found
-in the session.
-Finally,
-that user is used to check
-if the hash provided validates
-when compared to the user's hashed password
-(there is some extra hashing involved
-to ensure that the user's password hash is not stored directly
-in the session).
-If the comparison checks out,
-the user is authenticated
-and the request proceeds as an authenticated request.
-
-You can see that the session is vital
-to this flow with the auth system.
-Without the ability to store state
-in the session,
-the user would be unable
-to prove who they were.
-
-Another use of sessions is found
-with CSRF handling.
-The CSRF security features in Django
+* The string name of the auth backend used (stored in `_auth_user_backend`)
+
+Since we know that a session identifies a browser and does so securely, the auth system stores identity information into the session to tie that unique session to a unique user. When a user's browser makes an HTTP request, Django can determine the session associated with the request via the `sessionid` and gain access to the user auth data (i.e., the user's ID, hash, and auth backend). With these data elements, the auth system can determine if the request is valid and should be considered authenticated by checking against the associated auth session data.
+
+The auth system will read which backend is used and load that backend if possible. The backend is used to load the specific user record from the ID found in the session. Finally, that user is used to check if the hash provided validates when compared to the user's hashed password (there is some extra hashing involved to ensure that the user's password hash is not stored directly in the session). If the comparison checks out, the user is authenticated and the request proceeds as an authenticated request.
+
+You can see that the session is vital to this flow with the auth system. Without the ability to store state in the session, the user would be unable to prove who they were.
+
+Another use of sessions is found with CSRF handling. The CSRF security features in Django
{{< web >}}
(which I mentioned in the forms article
{{< /web >}}
{{< book >}}
(which I mentioned in the forms chapter
{{< /book >}}
-and we will explore more in a future topic)
-permit CSRF tokens to be stored
-in the session
-instead of a cookie
-when the `CSRF_USE_SESSIONS` setting is enabled.
-Django provides a safe default
-for CSRF tokens
-in cookies,
-but the session is an alternative storage place
-if you're not happy enough
-with the cookie configuration.
-
-As a final example,
-we can look
-at the `messages` application.
-The `messages` app can store "flash" messages.
-A flash message is the kind
-of message
-that you'd expect to see
-on a single page view.
-For instance,
-if you have a message
-that you'd like to display
-to a user upon some action,
-you might use a flash message.
-Perhaps your application has some "Contact Us" form
-to receive customer feedback.
-After the customer submits the form,
-you might want the application
-to flash "Thank you for the feedback!"
+and we will explore more in a future topic) permit CSRF tokens to be stored in the session instead of a cookie when the `CSRF_USE_SESSIONS` setting is enabled. Django provides a safe default for CSRF tokens in cookies, but the session is an alternative storage place if you're not happy enough with the cookie configuration.
+
+As a final example, we can look at the `messages` application. The `messages` app can store "flash" messages. A flash message is the kind of message that you'd expect to see on a single page view. For instance, if you have a message that you'd like to display to a user upon some action, you might use a flash message. Perhaps your application has some "Contact Us" form to receive customer feedback. After the customer submits the form, you might want the application to flash "Thank you for the feedback!":
```python
# application/views.py
@@ -455,20 +199,9 @@ class ContactView(FormView):
return super().form_valid(form)
```
-In the default setup,
-Django will attempt to store the flash message
-in the request's cookies,
-but, as we saw earlier,
-browsers constrain the maximum cookie size.
-If the flash messages will not fit in the request's cookies,
-then the `messages` app will switch
-to the session as a more robust alternative.
-Observe that this might run into problems
-if you are using the session's cookie storage engine!
-
-I hope that these examples from Django's `contrib` package provide you
-with some ideas for how you might use sessions
-in your own projects.
+In the default setup, Django will attempt to store the flash message in the request's cookies, but, as we saw earlier, browsers constrain the maximum cookie size. If the flash messages will not fit in the request's cookies, then the `messages` app will switch to the session as a more robust alternative. Observe that this might run into problems if you are using the session's cookie storage engine!
+
+I hope that these examples from Django's `contrib` package provide you with some ideas for how you might use sessions in your own projects.
## Summary
@@ -478,17 +211,15 @@ In this article,
{{< book >}}
In this chapter,
{{< /book >}}
-we dug into Django sessions
-and how you use them.
+we dug into Django sessions and how you use them.
+
We saw:
* What sessions are and the interface they expose as `request.session`
* How JSON is used to manage session data
-* Different kinds of session storage
- that are available to your site
+* Different kinds of session storage that are available to your site
* The way that Django recognizes a user's session in the browser
-* Examples within `django.contrib`
- of how sessions get used by Django's built-in apps.
+* Examples within `django.contrib` of how sessions get used by Django's built-in apps.
{{< web >}}
In the next article,
@@ -496,8 +227,8 @@ In the next article,
{{< book >}}
In the next chapter,
{{< /book >}}
-we are going to spend time focusing
-on settings in Django.
+we are going to spend time focusing on settings in Django.
+
You'll learn about:
* Various strategies for managing your project's settings
@@ -505,15 +236,6 @@ You'll learn about:
* Tools in the larger Django ecosystem that can make your life easier
{{< web >}}
-If you'd like to follow along
-with the series,
-please feel free to sign up
-for my newsletter
-where I announce all of my new content.
-If you have other questions,
-you can reach me online
-on Twitter
-where I am
-{{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
+If you'd like to follow along with the series, please feel free to sign up for my newsletter where I announce all of my new content. If you have other questions, you can reach me online on Twitter where I am {{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
{{< /web >}}
From 8a6037821da352b18d94d44e58cce4f2f8cabab3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nazar=C3=A9=20da=20Piedade?=
Date: Fri, 5 May 2023 11:19:15 +0100
Subject: [PATCH 16/30] chore: format
`understand-django/2021-07-15-settings.pt.md`
---
.../2021-07-15-settings.pt.md | 679 +++---------------
1 file changed, 114 insertions(+), 565 deletions(-)
diff --git a/content/understand-django/2021-07-15-settings.pt.md b/content/understand-django/2021-07-15-settings.pt.md
index 4626e7ff..19caccb9 100644
--- a/content/understand-django/2021-07-15-settings.pt.md
+++ b/content/understand-django/2021-07-15-settings.pt.md
@@ -1,13 +1,7 @@
---
title: "Making Sense Of Settings"
description: >-
- All Django apps need to be configured
- in order to run properly.
- In this article,
- we will dig into how Django lets you configure your project
- using a settings module.
- We'll also look at ways to be extra effective
- with settings.
+ All Django apps need to be configured in order to run properly. In this article, we will dig into how Django lets you configure your project using a settings module. We'll also look at ways to be extra effective with settings.
image: img/django.png
type: post
categories:
@@ -23,21 +17,12 @@ series: "Understand Django"
In the last
{{< web >}}
-[Understand Django]({{< ref "/understand-django/_index.md" >}})
-article,
+[Understand Django]({{< ref "/understand-django/_index.pt.md" >}}) article,
{{< /web >}}
{{< book >}}
chapter,
{{< /book >}}
-we looked at a storage concept
-in Django
-called sessions.
-Sessions provide a solution to problems
-like
-"How does Django know
-when a user is logged in?" or
-"Where can the framework store data
-for a visitor on your app?"
+we looked at a storage concept in Django called sessions. Sessions provide a solution to problems like "How does Django know when a user is logged in?" or "Where can the framework store data for a visitor on your app?"
{{< web >}}
With this article,
@@ -45,115 +30,49 @@ With this article,
{{< book >}}
With this chapter,
{{< /book >}}
-you'll learn about Django settings
-and how to manage the configuration
-of your application.
-We'll also look at tools
-to help you
-define your settings effectively.
+you'll learn about Django settings and how to manage the configuration of your application. We'll also look at tools to help you define your settings effectively.
-{{< understand-django-series "settings" >}}
+{{< understand-django-series-pt "settings" >}}
## How Is Django Configured?
-To run properly,
-Django needs to be configured.
-We need to understand
-where this configuration comes from.
-Django has the ability
-to use default configuration values
-or values set
-by developers like yourself,
-but where does it get those from?
+To run properly, Django needs to be configured. We need to understand where this configuration comes from. Django has the ability to use default configuration values or values set by developers like yourself, but where does it get those from?
-Early in the process
-of starting a Django application,
-Django will internally import the following:
+Early in the process of starting a Django application, Django will internally import the following:
```python
from django.conf import settings
```
-This `settings` import is a package level object
-created in `django/conf/__init__.py`.
-The `settings` object has attributes added to it
-from two primary sources.
-
-The first source is a set of global default settings
-that come from the framework.
-These global settings are from `django/conf/global_settings.py`
-and provide a set of initial values
-for configuration
-that Django needs to operate.
-
-The second source of configuration settings comes
-from user defined values.
-Django will accept a Python module
-and apply its module level attributes
-to the `settings` object.
-To find the user module,
-Django searches for a `DJANGO_SETTINGS_MODULE` environment variable.
+This `settings` import is a package level object created in `django/conf/__init__.py`. The `settings` object has attributes added to it from two primary sources.
+
+The first source is a set of global default settings that come from the framework. These global settings are from `django/conf/global_settings.py` and provide a set of initial values for configuration that Django needs to operate.
+
+The second source of configuration settings comes from user defined values. Django will accept a Python module and apply its module level attributes to the `settings` object. To find the user module, Django searches for a `DJANGO_SETTINGS_MODULE` environment variable.
### Sidebar: Environment Variables
-Environment variables are not a Django concept.
-When any program runs
-on a computer,
-the operating system makes certain data available
-to the running program.
-This set of data is called the program's "environment,"
-and each piece of data
-in that set
-is an environment variable.
-
-If you're starting Django from a terminal,
-you can view the environment variables
-that Django will receive from the operating system
-by running the `env` command on macOS or Linux,
-or the `set` command on Windows.
-
-We can add our own environment variables
-to the environment
-with the `export` command on macOS or Linux,
-or the `set` command on Windows.
-Environment variables are typically named
-in all capital letters.
+Environment variables are not a Django concept. When any program runs on a computer, the operating system makes certain data available to the running program. This set of data is called the program's "environment," and each piece of data in that set is an environment variable.
+
+If you're starting Django from a terminal, you can view the environment variables that Django will receive from the operating system by running the `env` command on macOS or Linux, or the `set` command on Windows.
+
+We can add our own environment variables to the environment with the `export` command on macOS or Linux, or the `set` command on Windows. Environment variables are typically named in all capital letters:
```bash
$ export HELLO=world
```
-Now that we have a basic understanding
-of environment variables,
-let's return to the `DJANGO_SETTINGS_MODULE` variable.
-The variable's value should be the location
-of a Python module containing any settings
-that a developer wants to change
-from Django's default values.
-
-If you create a Django project
-with `startproject`
-and use `project` as the name,
-then you will find a generated settings file
-with the path `project/settings.py`.
-When Django runs,
-you could explicitly instruct Django with:
+Now that we have a basic understanding of environment variables, let's return to the `DJANGO_SETTINGS_MODULE` variable. The variable's value should be the location of a Python module containing any settings that a developer wants to change from Django's default values.
+
+If you create a Django project with `startproject` and use `project` as the name, then you will find a generated settings file with the path `project/settings.py`. When Django runs, you could explicitly instruct Django with:
```bash
$ export DJANGO_SETTINGS_MODULE=project.settings
```
-Instead of supplying the file path,
-the `DJANGO_SETTINGS_MODULE` should be
-in a Python module dotted notation.
+Instead of supplying the file path, the `DJANGO_SETTINGS_MODULE` should be in a Python module dotted notation.
-You may not actually need
-to set `DJANGO_SETTINGS_MODULE` explicitly.
-If you stick with the same settings file
-that is created by `startproject`,
-you can find a line
-in `wsgi.py`
-that looks like:
+You may not actually need to set `DJANGO_SETTINGS_MODULE` explicitly. If you stick with the same settings file that is created by `startproject`, you can find a line in `wsgi.py` that looks like:
```python
os.environ.setdefault(
@@ -162,27 +81,9 @@ os.environ.setdefault(
)
```
-Because of this line,
-Django will attempt to read
-from `project.settings`
-(or whatever you named your project)
-without the need to explicitly set `DJANGO_SETTINGS_MODULE`.
-Feel free to adjust the default value
-if you have a different settings file
-that you prefer to use
-for local development.
-
-Once Django reads the global settings
-and any user defined settings,
-we can get any configuration
-from the `settings` object
-via attribute access.
-This convention of keeping all configuration
-in the `settings` object
-is a convenient pattern
-that the framework,
-third party library ecosystem,
-and *you* can depend on.
+Because of this line, Django will attempt to read from `project.settings` (or whatever you named your project) without the need to explicitly set `DJANGO_SETTINGS_MODULE`. Feel free to adjust the default value if you have a different settings file that you prefer to use for local development.
+
+Once Django reads the global settings and any user defined settings, we can get any configuration from the `settings` object via attribute access. This convention of keeping all configuration in the `settings` object is a convenient pattern that the framework, third party library ecosystem, and *you* can depend on:
```python
$ ./manage.py shell
@@ -191,131 +92,51 @@ $ ./manage.py shell
'a secret to everybody'
```
-The `settings` object is a shared item
-so it is generally thought
-to be a Really Bad Idea™
-to edit and assign to the object directly.
-Keep your settings in your settings module!
+The `settings` object is a shared item so it is generally thought to be a Really Bad Idea™ to edit and assign to the object directly. Keep your settings in your settings module!
-That's the core of Django configuration.
-We're ready to focus
-on the user defined settings
-and our responsibilities
-as Django app developers.
+That's the core of Django configuration. We're ready to focus on the user defined settings and our responsibilities as Django app developers.
## Settings Module Patterns
-There are multiple ways to deal
-with settings modules
-and how to populate those modules
-with the appropriate values
-for different environments.
-Let's look at some popular patterns.
+There are multiple ways to deal with settings modules and how to populate those modules with the appropriate values for different environments. Let's look at some popular patterns.
### Multiple Modules Per Environment
-A Django settings module is a Python module.
-Nothing is stopping us
-from using the full power
-of Python
-to configure that module the way we want.
+A Django settings module is a Python module. Nothing is stopping us from using the full power of Python to configure that module the way we want.
-Minimally, you will probably have at least two environments
-where your Django app runs:
+Minimally, you will probably have at least two environments where your Django app runs:
* On your local machine while developing
* On the internet for your live site
-We should know by now
-that setting `DEBUG = True` is a terrible idea
-for a live Django site,
-so how can we get the benefits of the debug mode
-without having `DEBUG` set to `True`
-in our module?
+We should know by now that setting `DEBUG = True` is a terrible idea for a live Django site, so how can we get the benefits of the debug mode without having `DEBUG` set to `True` in our module?
-One technique is to use separate settings modules.
-With this strategy,
-you can pick which environment your Django app should run for
-by switching the `DJANGO_SETTINGS_MODULE` value
-to pick a different environment.
-You might have modules like:
+One technique is to use separate settings modules. With this strategy, you can pick which environment your Django app should run for by switching the `DJANGO_SETTINGS_MODULE` value to pick a different environment. You might have modules like:
* `project.settings.dev`
* `project.settings.stage`
* `project.settings.production`
-These examples would be for a local development environment
-on your laptop,
-a staging environment
-(which is a commonly used pattern
-for testing a site
-that is as similar to the live site as possible
-without *being* the live site),
-and a production environment.
+These examples would be for a local development environment on your laptop, a staging environment (which is a commonly used pattern for testing a site that is as similar to the live site as possible without *being* the live site), and a production environment.
{{< web >}}
As a reminder from the deployment article,
{{< /web >}}
{{< book >}}
As a reminder from the deployment chapter,
{{< /book >}}
-the software industry likes to call the primary site
-for customers "production."
-
-This strategy has certain challenges to consider.
-Should you replicate settings
-in each file
-or use some common module between them?
-
-If you decide to replicate the settings across modules,
-you'll have the advantage that the settings module shows *all*
-of the settings in a single place
-for that environment.
-The disadvantage is that keeping the common settings the same
-could be a challenge
-if you forget to update one of the modules.
-
-On the other hand,
-you could use a common module.
-The advantage to this form is that the common settings can be
-in a single location.
-The environment specific files only need to record the *differences*
-between the environments.
-The disadvantage is that it is harder to get a clear picture
-of all the settings of that environment.
-
-If you decide to use a common module,
-this style is often implemented
-with a `*` import.
-I can probably count on one hand the number
-of places
-where I'm ok with a `*` import,
-and this is one of them.
-In most cases the Python community prefers explicit over implicit,
-and the idea extends to the treatment of imports.
-Explicit imports make it clear what a module is actually using.
-The `*` import is very implicit,
-and it makes it unclear what a module uses.
-For the case of a common settings module,
-a `*` import is actually positive
-because we want to use *everything*
-in the common module.
-
-Let's make this more concrete.
-Assume that you have a `project.settings.base` module.
-This module would hold your common settings
-for your app.
-I'd recommend that you try to make your settings safe and secure
-by default.
-For instance,
-use `DEBUG = False` in the base settings
-and force other settings modules
-to opt-in
-to the more unsafe behavior.
-
-For your local development environment
-on your laptop,
-you could use `project.settings.dev`.
-This settings module would look like:
+the software industry likes to call the primary site for customers "production."
+
+This strategy has certain challenges to consider. Should you replicate settings in each file or use some common module between them?
+
+If you decide to replicate the settings across modules, you'll have the advantage that the settings module shows *all* of the settings in a single place for that environment. The disadvantage is that keeping the common settings the same could be a challenge if you forget to update one of the modules.
+
+On the other hand, you could use a common module. The advantage to this form is that the common settings can be in a single location. The environment specific files only need to record the *differences* between the environments. The disadvantage is that it is harder to get a clear picture of all the settings of that environment.
+
+If you decide to use a common module, this style is often implemented with a `*` import. I can probably count on one hand the number of places where I'm ok with a `*` import, and this is one of them. In most cases the Python community prefers explicit over implicit, and the idea extends to the treatment of imports. Explicit imports make it clear what a module is actually using. The `*` import is very implicit, and it makes it unclear what a module uses. For the case of a common settings module, a `*` import is actually positive because we want to use *everything* in the common module.
+
+Let's make this more concrete. Assume that you have a `project.settings.base` module. This module would hold your common settings for your app. I'd recommend that you try to make your settings safe and secure by default. For instance, use `DEBUG = False` in the base settings and force other settings modules to opt-in to the more unsafe behavior.
+
+For your local development environment on your laptop, you could use `project.settings.dev`. This settings module would look like:
```python
# project/settings/dev.py
@@ -328,63 +149,22 @@ DEBUG = True
...
```
-By using the `*` import in the `dev.py` file,
-all the settings from `base.py` are pulled
-into the module level scope.
-Where you want a setting to be different,
-you set the value in `dev.py`.
-When Django starts using `DJANGO_SETTINGS_MODULE`
-of `project.settings.dev`,
-all the values from `base.py` will be used
-via `dev.py`.
-
-This scheme gives you control to define common things once,
-but there is still a big challenge
-with this.
-What do we do about settings
-that need to be kept secret
-(e.g., API keys)?
-
-*Don't commit secret data to your code repository!*
-Adding secrets to your source control tool
-like Git
-is usually not a good idea.
-This is especially true
-if you have a public repository
-on GitHub.
-Think no one is paying attention to your repo?
-Think again!
-There are tools out there
-that scan *every public commit* made to GitHub.
-These tools are specifically looking
-for secret data
-to exploit.
-
-If you can't safely add secrets
-to your code repo,
-where can we add them instead?
-You can use environment variables!
-Let's look at another scheme
-for managing settings
-with environment variables.
+By using the `*` import in the `dev.py` file, all the settings from `base.py` are pulled into the module level scope. Where you want a setting to be different, you set the value in `dev.py`. When Django starts using `DJANGO_SETTINGS_MODULE` of `project.settings.dev`, all the values from `base.py` will be used via `dev.py`.
+
+This scheme gives you control to define common things once, but there is still a big challenge with this. What do we do about settings that need to be kept secret (e.g., API keys)?
+
+*Don't commit secret data to your code repository!* Adding secrets to your source control tool like Git is usually not a good idea. This is especially true if you have a public repository on GitHub. Think no one is paying attention to your repo? Think again! There are tools out there that scan *every public commit* made to GitHub. These tools are specifically looking for secret data to exploit.
+
+If you can't safely add secrets to your code repo, where can we add them instead? You can use environment variables! Let's look at another scheme for managing settings with environment variables.
### Settings Via Environment Variables
-In Python,
-you can access environment variables
-through the `os` module.
-The module contains the `environ` attribute,
-which functions like a dictionary.
+In Python, you can access environment variables through the `os` module. The module contains the `environ` attribute, which functions like a dictionary.
-By using environment variables,
-your settings module can get configuration settings
-from the external environment
-that is running the Django app.
-This is a solid pattern because it can accomplish two things:
+By using environment variables, your settings module can get configuration settings from the external environment that is running the Django app. This is a solid pattern because it can accomplish two things:
* Secret data can be kept out of your code
-* Configuration differences between environments
- can be managed by changing environment variable values
+* Configuration differences between environments can be managed by changing environment variable values
Here's an example of secret data management:
@@ -398,45 +178,20 @@ SECRET_KEY = os.environ['SECRET_KEY']
...
```
-Django needs a secret key
-for a variety of safe hashing purposes.
-There is a warning in the default `startproject` output
-that reads:
+Django needs a secret key for a variety of safe hashing purposes. There is a warning in the default `startproject` output that reads:
```python
# SECURITY WARNING: keep the secret key used in production secret!
```
-By moving the secret key value
-to an environment variable
-that happens to have a matching name of `SECRET_KEY`,
-we won't be committing the value
-to source control
-for some nefarious actor to discover.
-
-This pattern works really well for secrets,
-but it can also work well
-for *any* configuration
-that we want to vary between environments.
-
-For instance,
-on one of my projects,
-I use the excellent {{< extlink "https://anymail.readthedocs.io/en/stable/" "Anymail" >}} package
-to send emails
-via an email service provider
-(of the ESPs, I happen to use {{< extlink "https://sendgrid.com/" "SendGrid" >}}).
-When I'm working with my development environment,
-I don't want to send real email.
-Because of that,
-I use an environment variable
-to set Django's `EMAIL_BACKEND` setting.
-This lets me switch between the Anymail backend
-and Django's built-in
-`django.core.mail.backends.console.EmailBackend`
-that prints emails to the terminal instead.
-
-If I did this email configuration with `os.environ`,
-it would look like:
+By moving the secret key value to an environment variable that happens to have a matching name of `SECRET_KEY`, we won't be committing the value to source control for some nefarious actor to discover.
+
+This pattern works really well for secrets, but it can also work well for *any* configuration that we want to vary between environments.
+
+For instance, on one of my projects, I use the excellent {{< extlink "https://anymail.readthedocs.io/en/stable/" "Anymail" >}} package
+to send emails via an email service provider (of the ESPs, I happen to use {{< extlink "https://sendgrid.com/" "SendGrid" >}}). When I'm working with my development environment, I don't want to send real email. Because of that, I use an environment variable to set Django's `EMAIL_BACKEND` setting. This lets me switch between the Anymail backend and Django's built-in `django.core.mail.backends.console.EmailBackend` that prints emails to the terminal instead.
+
+If I did this email configuration with `os.environ`, it would look like:
```python
# project/settings.py
@@ -451,31 +206,9 @@ EMAIL_BACKEND = os.environ.get(
...
```
-I prefer to make my default settings closer
-to the live site context.
-This not only leads to safer behavior
-(because I have to explicitly opt-out
-of safer settings like switching from `DEBUG = False` to `DEBUG = True`),
-but it also means that my live site has less to configure.
-That's good because there are fewer chances
-to make configuration mistakes
-on the site that matters most:
-the one where my customers are.
-
-We need to be aware of a big gotcha
-with using environment variables.
-*Environment variables* are only available as a `str` type.
-This is something to be aware of
-because there will be times when you want a boolean settings value
-or some other *type* of data.
-In a situation
-where you need a different type,
-you have to coerce a `str`
-into the type you need.
-In other words,
-don't forget that every string
-except the empty string is truthy
-in Python:
+I prefer to make my default settings closer to the live site context. This not only leads to safer behavior (because I have to explicitly opt-out of safer settings like switching from `DEBUG = False` to `DEBUG = True`), but it also means that my live site has less to configure. That's good because there are fewer chances to make configuration mistakes on the site that matters most: the one where my customers are.
+
+We need to be aware of a big gotcha with using environment variables. *Environment variables* are only available as a `str` type. This is something to be aware of because there will be times when you want a boolean settings value or some other *type* of data. In a situation where you need a different type, you have to coerce a `str` into the type you need. In other words, don't forget that every string except the empty string is truthy in Python:
```python
>>> not_false = "False"
@@ -483,68 +216,19 @@ in Python:
True
```
-In the next section,
-we will see tools that help alleviate this typing problem.
-
-Note:
-As you learn more about settings,
-you will probably encounter advice
-that says to avoid using environment variables.
-This is well intentioned advice
-that highlights
-that there *is* some risk
-with using environment variables.
-With this kind of advice,
-you may read a recommendation
-for secret management tools
-like {{< extlink "https://www.vaultproject.io/" "HashiCorp Vault" >}}.
-These are good tools,
-but consider them a more advanced topic.
-In my opinion,
-using environment variables for secret management
-is a reasonably low risk storage mechanism.
+In the next section, we will see tools that help alleviate this typing problem.
+
+Note: As you learn more about settings, you will probably encounter advice that says to avoid using environment variables. This is well intentioned advice that highlights that there *is* some risk with using environment variables. With this kind of advice, you may read a recommendation for secret management tools like {{< extlink "https://www.vaultproject.io/" "HashiCorp Vault" >}}. These are good tools, but consider them a more advanced topic. In my opinion, using environment variables for secret management is a reasonably low risk storage mechanism.
## Settings Management Tools
-We can focus on two categories
-of tools
-that can help you manage your settings
-in Django:
-built-in tools and third party libraries.
-
-The built-in tool that is available to you
-is the `diffsettings` command.
-This tool makes it easy
-to see the computed settings
-of your module.
-Since settings can come
-from multiple files
-(including Django's `global_settings.py`)
-or environment variables,
-inspecting the settings output
-of `diffsettings` is more convenient
-than thinking through how a setting is set.
-
-By default,
-`diffsettings` will show a comparison
-of the settings module
-to the default Django settings.
-Settings that aren't in the defaults are marked
-with `###` after the value
-to indicate that they are different.
-
-I find that the default output is not the most useful mode.
-Instead,
-you can instruct `diffsettings`
-to output in a "unified" format.
-This format looks a lot more like a code diff.
-In addition,
-Django will colorize that output
-so that it's easier to see.
-Here's an example
-of some of the security settings
-by running `./manage.py diffsettings --output unified`
-for one of my projects.
+We can focus on two categories of tools that can help you manage your settings in Django: built-in tools and third party libraries.
+
+The built-in tool that is available to you is the `diffsettings` command. This tool makes it easy to see the computed settings of your module. Since settings can come from multiple files (including Django's `global_settings.py`) or environment variables, inspecting the settings output of `diffsettings` is more convenient than thinking through how a setting is set.
+
+By default, `diffsettings` will show a comparison of the settings module to the default Django settings. Settings that aren't in the defaults are marked with `###` after the value to indicate that they are different.
+
+I find that the default output is not the most useful mode. Instead, you can instruct `diffsettings` to output in a "unified" format. This format looks a lot more like a code diff. In addition, Django will colorize that output so that it's easier to see. Here's an example of some of the security settings by running `./manage.py diffsettings --output unified` for one of my projects:
```diff
- SECURE_HSTS_INCLUDE_SUBDOMAINS = False
@@ -553,14 +237,7 @@ for one of my projects.
+ SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
```
-Finally,
-I'll note that you can actually compare two separate settings modules.
-Let's say you wanted to compare settings
-between your development mode
-and your live site.
-Assuming your settings files have names
-like I described earlier,
-you could run something like:
+Finally, I'll note that you can actually compare two separate settings modules. Let's say you wanted to compare settings between your development mode and your live site. Assuming your settings files have names like I described earlier, you could run something like:
```bash
$ ./manage.py diffsettings \
@@ -569,16 +246,9 @@ $ ./manage.py diffsettings \
--output unified
```
-By using the `--default` flag,
-we instruct Django that `project.settings.dev` is the baseline
-for comparison.
-This version of the command will show where the two settings modules are different.
+By using the `--default` flag, we instruct Django that `project.settings.dev` is the baseline for comparison. This version of the command will show where the two settings modules are different.
-Django only includes this single tool for working
-with settings,
-but I hope you can see that it's really handy.
-Now let's talk about a useful third party library
-that can help you with settings.
+Django only includes this single tool for working with settings, but I hope you can see that it's really handy. Now let's talk about a useful third party library that can help you with settings.
{{< web >}}
Earlier in the article,
@@ -586,22 +256,13 @@ Earlier in the article,
{{< book >}}
Earlier in the chapter,
{{< /book >}}
-I noted that dealing with environment variables has the pitfall
-of working with string data for everything.
-Thankfully, there is a package
-that can help you work
-with environment variables.
-The project is called {{< extlink "https://django-environ.readthedocs.io/en/latest/" "django-environ" >}}.
-django-environ primarily does two important things
-that I value:
+I noted that dealing with environment variables has the pitfall of working with string data for everything. Thankfully, there is a package that can help you work with environment variables. The project is called {{< extlink "https://django-environ.readthedocs.io/en/latest/" "django-environ" >}}.
+django-environ primarily does two important things that I value:
* The package allows you to coerce strings into a desired data type.
-* The package will read from a file
- to load environment variables into your environment.
+* The package will read from a file to load environment variables into your environment.
-What does type coercion look like?
-With `django-environ`,
-you start with an `Env` object.
+What does type coercion look like? With `django-environ`, you start with an `Env` object:
```python
# project/settings.py
@@ -611,17 +272,9 @@ import environ
env = environ.Env()
```
-The keyword arguments to `Env` describe the different environment variables
-that you expect the app to process.
-The key is the name of the environment variable.
-The value is a two element tuple.
-The first tuple element is the type you want,
-and the second element is a default value
-if the environment variable doesn't exist.
+The keyword arguments to `Env` describe the different environment variables that you expect the app to process. The key is the name of the environment variable. The value is a two element tuple. The first tuple element is the type you want, and the second element is a default value if the environment variable doesn't exist.
-If you want to be able to control `DEBUG`
-from an environment variable,
-the settings would be:
+If you want to be able to control `DEBUG` from an environment variable, the settings would be:
```python
# project/settings.py
@@ -635,35 +288,11 @@ env = environ.Env(
DEBUG = env("DEBUG")
```
-With this setup,
-your app will be safe by default
-with `DEBUG` set to `False`,
-but you'll be able to override
-that via the environment.
-`django-environ` works with a handful
-of strings that it will accept as `True`
-such as "on", "yes", "true", and others
-(see the documentation for more details).
-
-Once you start using environment variables,
-you'll want a convenient way to set them
-when your app runs.
-Manually calling `export` for all your variables
-before running your app is a totally unsustainable way
-to run apps.
-
-The `Env` class comes with a handy class method named `read_env`.
-With this method,
-your app can read environment variables
-into `os.environ`
-from a file.
-Conventionally,
-this file is named `.env`,
-and the file contains a list of key/value pairs
-that you want as environment variables.
-Following our earlier example,
-here's how we could set our app
-to be in debug mode:
+With this setup, your app will be safe by default with `DEBUG` set to `False`, but you'll be able to override that via the environment. `django-environ` works with a handful of strings that it will accept as `True` such as "on", "yes", "true", and others (see the documentation for more details).
+
+Once you start using environment variables, you'll want a convenient way to set them when your app runs. Manually calling `export` for all your variables before running your app is a totally unsustainable way to run apps.
+
+The `Env` class comes with a handy class method named `read_env`. With this method, your app can read environment variables into `os.environ` from a file. Conventionally, this file is named `.env`, and the file contains a list of key/value pairs that you want as environment variables. Following our earlier example, here's how we could set our app to be in debug mode:
```env
# .env
@@ -685,90 +314,29 @@ env = environ.Env(
DEBUG = env("DEBUG")
```
-If you use a `.env` file,
-you will occasionally find a need to put secrets
-into this file
-for testing.
-Since the file can be a source for secrets,
-you should add this to `.gitignore`
-or ignore it
-in whatever version control system you use.
-As time goes on,
-the list of variables and settings will likely grow,
-so it's also a common pattern
-to create a `.env.example` file
-that you can use as a template
-in case you ever need to start
-with a fresh clone of your repository.
+If you use a `.env` file, you will occasionally find a need to put secrets into this file for testing. Since the file can be a source for secrets, you should add this to `.gitignore` or ignore it in whatever version control system you use. As time goes on, the list of variables and settings will likely grow, so it's also a common pattern to create a `.env.example` file that you can use as a template in case you ever need to start with a fresh clone of your repository.
## My Preferred Settings Setup
-Now we've looked at multiple strategies
-and tools for managing settings.
-I've used many of these schemes
-on various Django projects,
-so what is my preferred setup?
-
-For the majority of use cases,
-I find that working with `django-environ`
-in a single file is the best pattern
-in my experience.
-
-When I use this approach,
-I make sure that all of my settings favor a safe default configuration.
-This minimizes the configuration
-that I have to do for a live site.
-
-I like the flexibility
-of the pattern,
-and I find that I can quickly set certain configurations
-when developing.
-For instance,
-when I want to do certain kinds of testing
-like checking email rendering,
-I'll call something like:
+Now we've looked at multiple strategies and tools for managing settings. I've used many of these schemes on various Django projects, so what is my preferred setup?
+
+For the majority of use cases, I find that working with `django-environ` in a single file is the best pattern in my experience.
+
+When I use this approach, I make sure that all of my settings favor a safe default configuration. This minimizes the configuration that I have to do for a live site.
+
+I like the flexibility of the pattern, and I find that I can quickly set certain configurations when developing. For instance, when I want to do certain kinds of testing like checking email rendering, I'll call something like:
```bash
$ EMAIL_TESTING=on ./manage.py runserver
```
-My settings file has a small amount of configuration
-to alter the email settings
-to point emails
-to a local SMTP server tool
-called {{< extlink "https://github.com/mailhog/MailHog" "MailHog" >}}.
-Because I set an environment variable directly
-on my command line call,
-I can easily switch
-into a mode that sends email
-to MailHog
-for quick review.
-
-Overall,
-I like the environment variable approach,
-but I do use more than one settings file
-for one important scenario: testing.
-
-When I run my unit tests,
-I want to guarantee
-that certain conditions are always true.
-There are things
-that a test suite should never do
-in the vast majority of cases.
-Sending real emails is a good example.
-If I happen to configure my `.env`
-to test real emails for the local environment,
-I don't want my tests
-to send out an email accidentally.
-
-Thus,
-I create a separate testing settings file
-and configure my test runner (pytest)
-to use those settings.
-This settings file *does* mostly use the base environment,
-but I'll override some settings with explicit values.
-Here's how I protect myself
-from accidental live emails:
+My settings file has a small amount of configuration to alter the email settings to point emails to a local SMTP server tool called {{< extlink "https://github.com/mailhog/MailHog" "MailHog" >}}. Because I set an environment variable directly on my command line call, I can easily switch into a mode that sends email to MailHog for quick review.
+
+Overall, I like the environment variable approach, but I do use more than one settings file for one important scenario: testing.
+
+When I run my unit tests, I want to guarantee that certain conditions are always true. There are things that a test suite should never do in the vast majority of cases. Sending real emails is a good example. If I happen to configure my `.env` to test real emails for the local environment, I don't want my tests to send out an email accidentally.
+
+Thus, I create a separate testing settings file and configure my test runner (pytest) to use those settings. This settings file *does* mostly use the base environment, but I'll override some settings with explicit values. Here's how I protect myself from accidental live emails:
```python
# project/testing_settings.py
@@ -779,17 +347,9 @@ from .settings import *
EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend"
```
-Even though my `Env` will look for an `EMAIL_BACKEND` environment variable
-to configure that setting dynamically,
-the testing setting is hardcoded
-to make email sending accidents impossible.
+Even though my `Env` will look for an `EMAIL_BACKEND` environment variable to configure that setting dynamically, the testing setting is hardcoded to make email sending accidents impossible.
-The combination of a single file
-for most settings supplemented
-with a testing settings file
-for safety
-is the approach
-that has worked the best for me.
+The combination of a single file for most settings supplemented with a testing settings file for safety is the approach that has worked the best for me.
## Summary
@@ -799,9 +359,8 @@ In this article,
{{< book >}}
In this chapter,
{{< /book >}}
-you learned about Django settings
-and how to manage the configuration
-of your application.
+you learned about Django settings and how to manage the configuration of your application.
+
We covered:
* How Django is configured
@@ -814,9 +373,8 @@ In the next article,
{{< book >}}
In the next chapter,
{{< /book >}}
-we will look at how to handle files and media
-provided by users
-(e.g., profile pictures).
+we will look at how to handle files and media provided by users (e.g., profile pictures).
+
You'll learn about:
* How Django models maintain references to files
@@ -824,15 +382,6 @@ You'll learn about:
* Packages that can store files in various cloud services
{{< web >}}
-If you'd like to follow along
-with the series,
-please feel free to sign up
-for my newsletter
-where I announce all of my new content.
-If you have other questions,
-you can reach me online
-on Twitter
-where I am
-{{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
+If you'd like to follow along with the series, please feel free to sign up for my newsletter where I announce all of my new content. If you have other questions, you can reach me online on Twitter where I am {{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
{{< /web >}}
From 51d2f32426ac0ce22050bc0147332a2dad3abf25 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nazar=C3=A9=20da=20Piedade?=
Date: Fri, 5 May 2023 11:19:33 +0100
Subject: [PATCH 17/30] chore: format
`understand-django/2021-09-14-media-files.pt.md`
---
.../2021-09-14-media-files.pt.md | 393 ++++--------------
1 file changed, 75 insertions(+), 318 deletions(-)
diff --git a/content/understand-django/2021-09-14-media-files.pt.md b/content/understand-django/2021-09-14-media-files.pt.md
index 3480ec11..c3c5d85f 100644
--- a/content/understand-django/2021-09-14-media-files.pt.md
+++ b/content/understand-django/2021-09-14-media-files.pt.md
@@ -1,15 +1,7 @@
---
title: "User File Use"
description: >-
- Maybe your app needs to handle files
- from users
- like profile pictures.
- Accepting files from others is tricky
- to do safely.
- In this article,
- we'll see the tools
- that Django provides
- to manage files safely.
+ Maybe your app needs to handle files from users like profile pictures. Accepting files from others is tricky to do safely. In this article, we'll see the tools that Django provides to manage files safely.
image: img/django.png
type: post
categories:
@@ -25,19 +17,12 @@ series: "Understand Django"
In the last
{{< web >}}
-[Understand Django]({{< ref "/understand-django/_index.md" >}})
-article,
+[Understand Django]({{< ref "/understand-django/_index.pt.md" >}}) article,
{{< /web >}}
{{< book >}}
chapter,
{{< /book >}}
-you learned about Django settings
-and how to manage the configuration
-of your application.
-We also looked at tools
-to help you
-define settings
-effectively.
+you learned about Django settings and how to manage the configuration of your application. We also looked at tools to help you define settings effectively.
{{< web >}}
With this article,
@@ -45,18 +30,9 @@ With this article,
{{< book >}}
With this chapter,
{{< /book >}}
-we're going to dig into file management.
-Unlike the static files
-that you create for the app yourself,
-you may want your app to accept files
-from your users.
-Profile pictures are a good example
-of user files.
-You'll see how Django handles those kinds
-of files
-and how to deal with them safely.
-
-{{< understand-django-series "files" >}}
+we're going to dig into file management. Unlike the static files that you create for the app yourself, you may want your app to accept files from your users. Profile pictures are a good example of user files. You'll see how Django handles those kinds of files and how to deal with them safely.
+
+{{< understand-django-series-pt "files" >}}
## Files In Django Models
@@ -66,61 +42,27 @@ As we saw in the models article,
{{< book >}}
As we saw in the models chapter,
{{< /book >}}
-model fields in a Django model map
-to a column in a database table.
-When you want to access the *data*
-for a model instance,
-Django will pull the data
-from a database row.
-
-Dealing with files in models is a bit different.
-While it *is* possible to store file data directly
-in a database,
-you won't see that happen often.
-The reason is that storing the data in the database
-usually affects the performance
-of the database,
-especially with a large number of files.
-
-Instead,
-a common pattern
-in database usage
-is to store files separately
-from the database itself.
-Within the database,
-a column would store some kind of *reference*
-to the stored file
-like a path
-if files are stored on a filesystem.
-This is the approach
-that Django takes
-with files.
-
-Now that you know that Django takes this approach,
-you can remember:
+model fields in a Django model map to a column in a database table. When you want to access the *data* for a model instance, Django will pull the data from a database row.
+
+Dealing with files in models is a bit different. While it *is* possible to store file data directly in a database, you won't see that happen often. The reason is that storing the data in the database usually affects the performance of the database, especially with a large number of files.
+
+Instead, a common pattern in database usage is to store files separately from the database itself. Within the database, a column would store some kind of *reference* to the stored file like a path if files are stored on a filesystem. This is the approach that Django takes with files.
+
+Now that you know that Django takes this approach, you can remember:
1. Django models hold the *reference* to a file (e.g., a file path)
2. The file *data* (i.e., the file itself) is stored somewhere else.
-The "somewhere else" is called the "file storage,"
-and we'll discuss storage
-in more depth
-in the next section.
+The "somewhere else" is called the "file storage," and we'll discuss storage in more depth in the next section.
-Let's focus on the first item.
-What do you use to reference the files?
-Like all other model data,
-we'll use a field!
-Django includes two fields
-that help with file management:
+Let's focus on the first item. What do you use to reference the files? Like all other model data, we'll use a field! Django includes two fields that help with file management:
* `FileField`
* `ImageField`
### `FileField`
-What if you want to store a profile picture?
-You might do something like this:
+What if you want to store a profile picture? You might do something like this:
```python
# application/models.py
@@ -132,10 +74,7 @@ class Profile(models.Model):
# Other fields like a OneToOneKey to User ...
```
-This is the most basic version of using file fields.
-We can use this model very directly
-with a Django shell
-to illustrate file management.
+This is the most basic version of using file fields. We can use this model very directly with a Django shell to illustrate file management:
```python
$ ./manage.py shell
@@ -149,27 +88,13 @@ $ ./manage.py shell
... )
```
-In this example,
-I'm creating a profile instance manually.
-There are a few interesting notes:
-
-* The `File` class is an important wrapper
- that Django uses
- to make Python file objects (i.e., the value returned from `open`) work
- with the storage system.
-* The name `image.png` and `my-image.png` do not have to match.
- Django can store the content of `image.png`
- and use `my-image.png`
- as the name to reference
- within the storage system.
-* Saving the picture will automatically save the parent model instance
- by default.
-
-More often than not,
-you won't need to use these interfaces directly
-because Django has form fields
-and other tools
-that manage much of this for you.
+In this example, I'm creating a profile instance manually. There are a few interesting notes:
+
+* The `File` class is an important wrapper that Django uses to make Python file objects (i.e., the value returned from `open`) work with the storage system.
+* The name `image.png` and `my-image.png` do not have to match. Django can store the content of `image.png` and use `my-image.png` as the name to reference within the storage system.
+* Saving the picture will automatically save the parent model instance by default.
+
+More often than not, you won't need to use these interfaces directly because Django has form fields and other tools that manage much of this for you.
The current model example raises questions.
@@ -177,25 +102,9 @@ The current model example raises questions.
* What if we have a name conflict between two files like "`my-image.png`"?
* What happens if we try to save something that isn't an image?
-If we make no changes to the current setup,
-the data will go into the root
-of the media file storage.
-Media file storage is a topic that will be covered later.
-For the moment,
-recognize that putting all the files into a single place (i.e., the root)
-will be a mess.
-This mess will be pronounced if you're trying to track many file fields,
-but we can fix this with the `upload_to` field keyword argument.
-The simplest version of `upload_to` can take a string
-that the storage logic will use as a directory prefix
-to scope content
-into a different area.
-
-We're still left with potentially conflicting filenames.
-Thankfully,
-`upload_to` can also accept a callable
-that gives us a chance to fix that issue.
-Let's rework the example.
+If we make no changes to the current setup, the data will go into the root of the media file storage. Media file storage is a topic that will be covered later. For the moment, recognize that putting all the files into a single place (i.e., the root) will be a mess. This mess will be pronounced if you're trying to track many file fields, but we can fix this with the `upload_to` field keyword argument. The simplest version of `upload_to` can take a string that the storage logic will use as a directory prefix to scope content into a different area.
+
+We're still left with potentially conflicting filenames. Thankfully, `upload_to` can also accept a callable that gives us a chance to fix that issue. Let's rework the example:
```python
# application/models.py
@@ -221,62 +130,21 @@ class Profile(models.Model):
# Other fields like a OneToOneKey to User ...
```
-With this new version
-of the profile model,
-all of the images will be stored
-in a `profile_pics` path
-within the file storage.
-
-This version also solves the duplicate filename problem.
-`profile_pic_path` ignores most
-of the original filename provided.
-If two users both happen to upload `profile-pic.jpg`,
-`profile_pic_path` will assign those images random IDs
-and ignore the `profile-pic` part
-of the filename.
-
-You can see that the function calls `uuid4()`.
-These are effectively random IDs called
-{{< extlink "https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_(random)" "Universally Unique Identifiers (UUID)" >}}.
-UUIDs are likely something that you've seen before
-if you've worked with computers long enough,
-even if you didn't know their name.
-An example UUID would be `76ee4ae4-8659-4b50-a04f-e222df9a656a`.
-In the storage area,
-you might find a file stored as:
+With this new version of the profile model, all of the images will be stored in a `profile_pics` path within the file storage.
+
+This version also solves the duplicate filename problem. `profile_pic_path` ignores most of the original filename provided. If two users both happen to upload `profile-pic.jpg`, `profile_pic_path` will assign those images random IDs and ignore the `profile-pic` part of the filename.
+
+You can see that the function calls `uuid4()`. These are effectively random IDs called {{< extlink "https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_(random)" "Universally Unique Identifiers (UUID)" >}}. UUIDs are likely something that you've seen before if you've worked with computers long enough, even if you didn't know their name. An example UUID would be `76ee4ae4-8659-4b50-a04f-e222df9a656a`. In the storage area, you might find a file stored as:
```text
profile_pics/76ee4ae4-8659-4b50-a04f-e222df9a656a.jpg
```
-Each call to `uuid4()` is nearly certain
-to generate a unique value.
-Because of this feature,
-we can avoid filename conflicts
-by storing profile pictures
-with a unique name.
-As an aside,
-UUIDs are not very friendly for users,
-so if you plan to let your users download these files,
-you might wish to explore alternative naming techniques.
-
-There's one more problem to fix
-in this example.
-How do we know that a user provided a valid image file?
-This is important to check,
-because we want to avoid storing malicious files
-that bad actors might upload
-to our apps.
-
-This is where the `ImageField` has value.
-This field type contains extra validation logic
-that can check the *content* of the file
-to check that the file is, in fact, an image.
-To use `ImageField`,
-you'll need to install the
-{{< extlink "https://pillow.readthedocs.io/en/latest/" "Pillow" >}} library.
-Pillow is a package
-that lets Python work with image data.
+Each call to `uuid4()` is nearly certain to generate a unique value. Because of this feature, we can avoid filename conflicts by storing profile pictures with a unique name. As an aside, UUIDs are not very friendly for users, so if you plan to let your users download these files, you might wish to explore alternative naming techniques.
+
+There's one more problem to fix in this example. How do we know that a user provided a valid image file? This is important to check, because we want to avoid storing malicious files that bad actors might upload to our apps.
+
+This is where the `ImageField` has value. This field type contains extra validation logic that can check the *content* of the file to check that the file is, in fact, an image. To use `ImageField`, you'll need to install the {{< extlink "https://pillow.readthedocs.io/en/latest/" "Pillow" >}} library. Pillow is a package that lets Python work with image data.
Our final example looks like:
@@ -304,55 +172,25 @@ class Profile(models.Model):
# Other fields like a OneToOneKey to User ...
```
-Now that we've seen how Django will track files and images
-in your models,
-let's go deeper
-and try to understand the file storage features.
+Now that we've seen how Django will track files and images in your models, let's go deeper and try to understand the file storage features.
## Files Under The Hood
-We now know that models store references to files
-and not the files themselves.
-The file storage task is delegated
-to a special Python class
-in the system.
-
-This Python class must implement
-{{< extlink "https://docs.djangoproject.com/en/4.1/ref/files/storage/" "a specific API" >}}.
-Why?
-Like so many other parts of Django,
-the storage class can be swapped out
-for a different class.
-We've seen this swappable pattern already
-with templates, databases, authentication, static files, and sessions.
-
-The setting to control which type
-of file storage Django uses is `DEFAULT_FILE_STORAGE`.
-This setting is a Python module path string
-to the specific class.
-
-So, what's the default?
-The default is a storage class
-that will store files locally
-on the server
-that runs the app.
-This is found at `django.core.files.storage.FileSystemStorage`.
-The storage class uses a couple
-of important settings:
-`MEDIA_ROOT` and `MEDIA_URL`.
-
-The `MEDIA_ROOT` setting defines
-where Django should look for files in the filesystem.
+We now know that models store references to files and not the files themselves. The file storage task is delegated to a special Python class in the system.
+
+This Python class must implement {{< extlink "https://docs.djangoproject.com/en/4.1/ref/files/storage/" "a specific API" >}}. Why? Like so many other parts of Django, the storage class can be swapped out for a different class. We've seen this swappable pattern already with templates, databases, authentication, static files, and sessions.
+
+The setting to control which type of file storage Django uses is `DEFAULT_FILE_STORAGE`. This setting is a Python module path string to the specific class.
+
+So, what's the default? The default is a storage class that will store files locally on the server that runs the app. This is found at `django.core.files.storage.FileSystemStorage`. The storage class uses a couple of important settings: `MEDIA_ROOT` and `MEDIA_URL`.
+
+The `MEDIA_ROOT` setting defines where Django should look for files in the filesystem:
```python
MEDIA_ROOT = BASE_DIR / "media"
```
-On my computer,
-with the above setting
-and the `Profile` class example
-from earlier,
-Django would store a file somewhere like:
+On my computer, with the above setting and the `Profile` class example from earlier, Django would store a file somewhere like:
```text
# This path is split to be easier to read.
@@ -361,11 +199,7 @@ Django would store a file somewhere like:
76ee4ae4-8659-4b50-a04f-e222df9a656a.jpg
```
-The other setting important to `FileSystemStorage` is `MEDIA_URL`.
-This setting will determine how files are accessed
-by browsers
-when Django is running.
-Let's say `MEDIA_URL` is:
+The other setting important to `FileSystemStorage` is `MEDIA_URL`. This setting will determine how files are accessed by browsers when Django is running. Let's say `MEDIA_URL` is:
```python
MEDIA_URL = "/media/"
@@ -380,9 +214,7 @@ Our profile picture would have a URL like:
'/media/profile_pics/76ee4ae4-8659-4b50-a04f-e222df9a656a.jpg'
```
-This is the path that we can reference
-in templates.
-An image tag template fragment would look like:
+This is the path that we can reference in templates. An image tag template fragment would look like:
{{< web >}}
```django
@@ -395,108 +227,42 @@ An image tag template fragment would look like:
```
{{< /book >}}
-The Django documentation shows how file storage is a specific interface.
-`FileSystemStorage` happens to be included
-with Django and implements this interface
-for the simplest storage mechanism,
-the file system
-of your server's operating system.
+The Django documentation shows how file storage is a specific interface. `FileSystemStorage` happens to be included with Django and implements this interface for the simplest storage mechanism, the file system of your server's operating system.
-We can also store files separately
-from the web server,
-and there are often really good reasons to do that.
-Up next,
-we'll look at another option
-for file storage aside from the provided default.
+We can also store files separately from the web server, and there are often really good reasons to do that. Up next, we'll look at another option for file storage aside from the provided default.
## Recommended Package
-What is a problem
-that can arise
-if you use the built-in `FileSystemStorage`
-to store files
-for your application?
-There are actually many possible problems!
-Here are a few:
+What is a problem that can arise if you use the built-in `FileSystemStorage` to store files for your application? There are actually many possible problems! Here are a few:
* The web server can have too many files and run out of disk space.
-* Users may upload malicious files
- to attempt to gain control
- of your server.
-* Users can upload large files
- that can cause a Denial of Service (DOS) attack
- and make your site inaccessible.
-
-If you conclude that `FileSystemStorage` will not work
-for your app,
-is there another good option?
-Absolutely!
-
-The most popular storage package
-to reach for is
-{{< extlink "https://django-storages.readthedocs.io/en/latest/" "django-storages" >}}.
-django-storages includes a set of storage classes
-that can connect
-to a variety
-of cloud services.
-These cloud services are able to store an arbitrary number of files.
-With django-storages,
-your application can connect to services like:
+* Users may upload malicious files to attempt to gain control of your server.
+* Users can upload large files that can cause a Denial of Service (DOS) attack and make your site inaccessible.
+
+If you conclude that `FileSystemStorage` will not work for your app, is there another good option? Absolutely!
+
+The most popular storage package to reach for is {{< extlink "https://django-storages.readthedocs.io/en/latest/" "django-storages" >}}. django-storages includes a set of storage classes that can connect to a variety of cloud services. These cloud services are able to store an arbitrary number of files. With django-storages, your application can connect to services like:
* Amazon Simple Storage Service (S3)
* Google Cloud Storage
* Digital Ocean Spaces
* Services you run separately like an SFTP server
-These services would have additional cost
-beyond the cost of running your web server
-in the cloud,
-but the services usually have shockingly low rates
-and some offer a generous free tier
-for lower levels of data storage.
+These services would have additional cost beyond the cost of running your web server in the cloud, but the services usually have shockingly low rates and some offer a generous free tier for lower levels of data storage.
Why use django-storages?
-* You will never need to worry about disk space.
- The cloud services offer effectively unlimited storage space
- if you're willing to pay for it.
-* The files will be separated from your Django web server.
- This can eliminate some categories of security problems
- like a malicious file trying to execute arbitrary code
- on the web server.
-* Cloud storage can offer some caching benefits
- and be easily connected to Content Delivery Networks
- to optimize how files are served to your app's users.
-
-As with all software choices,
-we have tradeoffs to consider
-when using different storage classes.
-On its face,
-django-storages seems to be nearly all positives.
-The benefits come with some setup complexity cost.
-
-For instance,
-I like to use Amazon S3
-for file storage.
-You can see from the
-{{< extlink "https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html" "Amazon S3 setup" >}} documentation
-that there is a fair amount of work to do
-beyond setting a different `DEFAULT_FILE_STORAGE` class.
-This setup includes setting AWS private keys,
-access controls,
-regions,
-buckets,
-and a handful of other important settings.
-
-While the setup cost exists,
-you'll usually pay that cost at the beginning
-of a project
-and be mostly hands off after that.
-
-django-storages is a pretty fantastic package,
-so if your project has a lot of files to manage,
-you should definitely consider using it
-as an alternative to the `FileSystemStorage`.
+* You will never need to worry about disk space. The cloud services offer effectively unlimited storage space if you're willing to pay for it.
+* The files will be separated from your Django web server. This can eliminate some categories of security problems like a malicious file trying to execute arbitrary code on the web server.
+* Cloud storage can offer some caching benefits and be easily connected to Content Delivery Networks to optimize how files are served to your app's users.
+
+As with all software choices, we have tradeoffs to consider when using different storage classes. On its face, django-storages seems to be nearly all positives. The benefits come with some setup complexity cost.
+
+For instance, I like to use Amazon S3 for file storage. You can see from the {{< extlink "https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html" "Amazon S3 setup" >}} documentation that there is a fair amount of work to do beyond setting a different `DEFAULT_FILE_STORAGE` class. This setup includes setting AWS private keys, access controls, regions, buckets, and a handful of other important settings.
+
+While the setup cost exists, you'll usually pay that cost at the beginning of a project and be mostly hands off after that.
+
+django-storages is a pretty fantastic package, so if your project has a lot of files to manage, you should definitely consider using it as an alternative to the `FileSystemStorage`.
## Summary
@@ -507,6 +273,7 @@ In this article,
In this chapter,
{{< /book >}}
you learned about Django file management.
+
We covered:
* How Django models maintain references to files
@@ -519,9 +286,8 @@ In the next article,
{{< book >}}
In the next chapter,
{{< /book >}}
-let's explore commands.
-Commands are the code
-that you can run with `./manage.py`.
+let's explore commands. Commands are the code that you can run with `./manage.py`.
+
You'll learn about:
* Built-in commands provided by Django
@@ -529,15 +295,6 @@ You'll learn about:
* Extra commands from the community that are useful extensions for apps
{{< web >}}
-If you'd like to follow along
-with the series,
-please feel free to sign up
-for my newsletter
-where I announce all of my new content.
-If you have other questions,
-you can reach me online
-on Twitter
-where I am
-{{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
+If you'd like to follow along with the series, please feel free to sign up for my newsletter where I announce all of my new content. If you have other questions, you can reach me online on Twitter where I am {{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
{{< /web >}}
From 6f354568043c1900d6fb7d995a4ba9ce23ae2ef7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nazar=C3=A9=20da=20Piedade?=
Date: Fri, 5 May 2023 11:19:57 +0100
Subject: [PATCH 18/30] chore: format
`understand-django/2021-11-04-command-apps.pt.md`
---
.../2021-11-04-command-apps.pt.md | 473 ++++--------------
1 file changed, 88 insertions(+), 385 deletions(-)
diff --git a/content/understand-django/2021-11-04-command-apps.pt.md b/content/understand-django/2021-11-04-command-apps.pt.md
index b48b0444..07e7fafc 100644
--- a/content/understand-django/2021-11-04-command-apps.pt.md
+++ b/content/understand-django/2021-11-04-command-apps.pt.md
@@ -1,12 +1,7 @@
---
title: "Command Your App"
description: >-
- With this Understand Django article,
- you'll learn about commands.
- Commands are the way to execute scripts
- that interact with your Django app.
- We'll see built-in commands
- and how to build your own commands.
+ With this Understand Django article, you'll learn about commands. Commands are the way to execute scripts that interact with your Django app. We'll see built-in commands and how to build your own commands.
image: img/django.png
type: post
categories:
@@ -22,15 +17,12 @@ series: "Understand Django"
In the last
{{< web >}}
-[Understand Django]({{< ref "/understand-django/_index.md" >}})
-article,
+[Understand Django]({{< ref "/understand-django/_index.pt.md" >}}) article,
{{< /web >}}
{{< book >}}
chapter,
{{< /book >}}
-we dug into file management.
-We saw how Django handles user uploaded files
-and how to deal with them safely.
+we dug into file management. We saw how Django handles user uploaded files and how to deal with them safely.
{{< web >}}
With this article,
@@ -38,21 +30,13 @@ With this article,
{{< book >}}
With this chapter,
{{< /book >}}
-you'll learn about commands.
-Commands are the way to execute scripts
-that interact with your Django app.
-We'll see built-in commands
-and how to build your own.
+you'll learn about commands. Commands are the way to execute scripts that interact with your Django app. We'll see built-in commands and how to build your own.
-{{< understand-django-series "commands" >}}
+{{< understand-django-series-pt "commands" >}}
## Why Commands?
-Django makes it possible
-to run code from a terminal
-with `./manage.py`,
-but why is this helpful or needed?
-Consider this script:
+Django makes it possible to run code from a terminal with `./manage.py`, but why is this helpful or needed? Consider this script:
```python
# product_stat.py
@@ -68,11 +52,7 @@ which you could try running with:
$ python product_stat.py
```
-The problem with this script is that Django is not ready to run yet.
-If you tried to run this kind of code,
-you would get an `ImproperlyConfigured` exception.
-There are a couple of modifications
-that you could make to get the script to run.
+The problem with this script is that Django is not ready to run yet. If you tried to run this kind of code, you would get an `ImproperlyConfigured` exception. There are a couple of modifications that you could make to get the script to run:
* Call `django.setup()`.
* Specify the `DJANGO_SETTINGS_MODULE`.
@@ -88,53 +68,32 @@ from application.models import Product
print(Product.objects.count())
```
-Note that `django.setup()` must be before your Django related imports
-(like the `Product` model in this example).
-Now the script can run if you supply where the settings are located too.
+Note that `django.setup()` must be before your Django related imports (like the `Product` model in this example). Now the script can run if you supply where the settings are located too:
```bash
$ DJANGO_SETTING_MODULE=project.settings python product_stat.py
```
-This arrangement is less than ideal,
-but why else might we want a way to run commands through Django?
+This arrangement is less than ideal, but why else might we want a way to run commands through Django?
Try running `./manage.py -h`.
-What you'll likely see is more commands
-than what Django provides alone.
-This is where we begin to see more value
-from the command system.
-Because Django provides a standard way
-to run scripts,
-other Django applications can bundle useful commands
-and make them easily accessible
-to you.
-
-Now that you've had a chance to see why commands exist
-to run scripts
-for Django apps,
-let's back up and see *what* commands are.
+What you'll likely see is more commands than what Django provides alone. This is where we begin to see more value from the command system. Because Django provides a standard way to run scripts, other Django applications can bundle useful commands and make them easily accessible to you.
+
+Now that you've had a chance to see why commands exist to run scripts for Django apps, let's back up and see *what* commands are.
## We Hereby Command
-Django gives us a tool to run commands
-before we've even started our project.
-That tool is the `django-admin` script.
-We saw it all the way back
+Django gives us a tool to run commands before we've even started our project. That tool is the `django-admin` script. We saw it all the way back
{{< web >}}
in the first article
{{< /web >}}
{{< book >}}
in the first chapter
{{< /book >}}
-where I provided a short set
-of setup instructions
-to get you started
-if you've never used Django before.
+where I provided a short set of setup instructions to get you started if you've never used Django before.
-After you've started a project,
-your code will have a `manage.py` file,
+After you've started a project, your code will have a `manage.py` file,
{{< web >}}
and the commands you've seen in most articles are in the form of:
{{< /web >}}
@@ -146,17 +105,9 @@ and the commands you've seen in most chapters are in the form of:
$ ./manage.py some_command
```
-What's the difference
-between `django-admin` and `manage.py`?
-In truth, **not much!**
+What's the difference between `django-admin` and `manage.py`? In truth, **not much!**
-`django-admin` comes from Django's Python packaging.
-In Python packages,
-package developers can create scripts
-by defining an entry point
-in the {{< extlink "https://github.com/django/django/blob/4.1/setup.cfg" "packaging configuration" >}}.
-In Django,
-this configuration looks like:
+`django-admin` comes from Django's Python packaging. In Python packages, package developers can create scripts by defining an entry point in the {{< extlink "https://github.com/django/django/blob/4.1/setup.cfg" "packaging configuration" >}}. In Django, this configuration looks like:
```ini
[options.entry_points]
@@ -164,9 +115,7 @@ console_scripts =
django-admin = django.core.management:execute_from_command_line
```
-Meanwhile,
-the entire `manage.py`
-of a Django project looks like:
+Meanwhile, the entire `manage.py` of a Django project looks like:
```python
#!/usr/bin/env python
@@ -192,28 +141,11 @@ if __name__ == '__main__':
main()
```
-If you look closely,
-you can see that the different scripts are both ways
-to invoke the `execute_from_command_line` function
-to run a command.
-The primary difference is that the latter script will attempt
-to set the `DJANGO_SETTINGS_MODULE` environment variable automatically.
-Since Django needs to have `DJANGO_SETTINGS_MODULE` defined
-for most commands
-(note: `startproject` does *not* require that variable),
-`manage.py` is a more convenient way
-to run commands.
-
-`execute_from_command_line` is able to present
-what commands are available to a project,
-whether a command comes from Django itself,
-an installed app,
-or is a custom command that you created yourself.
-How are the commands discovered?
-*The command system does discovery by following some packaging conventions.*
-
-Let's say your project has an app named `application`.
-Django can find the command if you have the following packaging structure.
+If you look closely, you can see that the different scripts are both ways to invoke the `execute_from_command_line` function to run a command. The primary difference is that the latter script will attempt to set the `DJANGO_SETTINGS_MODULE` environment variable automatically. Since Django needs to have `DJANGO_SETTINGS_MODULE` defined for most commands (note: `startproject` does *not* require that variable), `manage.py` is a more convenient way to run commands.
+
+`execute_from_command_line` is able to present what commands are available to a project, whether a command comes from Django itself, an installed app, or is a custom command that you created yourself. How are the commands discovered? *The command system does discovery by following some packaging conventions.*
+
+Let's say your project has an app named `application`. Django can find the command if you have the following packaging structure:
```text
application
@@ -236,107 +168,31 @@ $ ./manage.py custom_command
Notes:
-* Django will create a command for a module found
- in `/management/commands/.py`.
-* Don't forget the `__init__.py` files!
- Django can only discover the commands
- if `management` and `commands` are proper Python package directories.
-* The example uses `custom_command`,
- but you can name your command
- with whatever valid Python module name that you want.
-
-Unfortunately,
-we can't slap some Python code
-into `custom_command.py`
-and assume that Django will know how to run it.
-Within the `custom_command.py` module,
-Django needs to find a `Command` class
-that subclasses a `BaseCommand` class
-that is provided by the framework.
-Django requires this structure to give command authors a consistent way
-to access features
-of the command system.
-
-With the `Command` class,
-you can add a `help` class attribute.
-Adding help can give users a description
-of what your command does when running
-`./manage.py custom_command -h`.
-
-The `Command` class will also help you with handling arguments.
-If your command needs to work with user input,
-you'll need to parse that data.
-Thankfully,
-the class integrates
-with Python's built-in `argparse` module.
-By including an `add_arguments` method,
-a command can parse the data
-and pass the results
-to the command's handler method in a structured way.
-If you've had to write Python scripts before,
-then you may understand how much time this kind of parsing can save you
-(and for those who haven't, the answer is "a lot of time!").
-
-Other smaller features exist within the `Command` class too.
-Perhaps you only want your command to run
-if your project has satisfied certain pre-conditions.
-Commands can use the `requires_migration_checks`
-or `requires_system_checks`
-to ensure that the system is in the correct state
-before running.
-
-I hope it's clear
-that the goal of the `Command` class is
-to help you with common actions
-that many commands will need to use.
-There is a small API to learn,
-but the system is a boon to making scripts quickly.
+* Django will create a command for a module found in `/management/commands/.py`.
+* Don't forget the `__init__.py` files! Django can only discover the commands if `management` and `commands` are proper Python package directories.
+* The example uses `custom_command`, but you can name your command with whatever valid Python module name that you want.
+
+Unfortunately, we can't slap some Python code into `custom_command.py` and assume that Django will know how to run it. Within the `custom_command.py` module, Django needs to find a `Command` class that subclasses a `BaseCommand` class that is provided by the framework. Django requires this structure to give command authors a consistent way to access features of the command system.
+
+With the `Command` class, you can add a `help` class attribute. Adding help can give users a description of what your command does when running `./manage.py custom_command -h`.
+
+The `Command` class will also help you with handling arguments. If your command needs to work with user input, you'll need to parse that data. Thankfully, the class integrates with Python's built-in `argparse` module. By including an `add_arguments` method, a command can parse the data and pass the results to the command's handler method in a structured way. If you've had to write Python scripts before, then you may understand how much time this kind of parsing can save you (and for those who haven't, the answer is "a lot of time!").
+
+Other smaller features exist within the `Command` class too. Perhaps you only want your command to run if your project has satisfied certain pre-conditions. Commands can use the `requires_migration_checks` or `requires_system_checks` to ensure that the system is in the correct state before running.
+
+I hope it's clear that the goal of the `Command` class is to help you with common actions that many commands will need to use. There is a small API to learn, but the system is a boon to making scripts quickly.
## Command By Example
-Let's consider a powerful use case
-to see a command in action.
-When you initially start a Django app,
-all of your app's interaction will probably be through web pages.
-After all,
-you were trying to use Django to make a web app,
-right?
-What do you do when you need to do something
-that doesn't involve a browser?
-
-This kind of work for your app is often considered *background* work.
-Background work is a pretty deep topic
-and will often involve special background task software
-like
-{{< extlink "https://docs.celeryproject.org/en/stable/getting-started/introduction.html" "Celery" >}}.
-When your app is at an early stage,
-Celery or similar software can be overkill and far more than you need.
-
-A simpler alternative for some background tasks could be a command paired
-with a scheduling tool like
-{{< extlink "https://en.wikipedia.org/wiki/Cron" "cron" >}}.
-
-On one of my projects,
-I offer free trials for accounts.
-After 60 days,
-the free trial ends
-and users either need to pay
-for the service
-or discontinue using it.
-By using a command
-and pairing it
-with the
-{{< extlink "https://devcenter.heroku.com/articles/scheduler" "Heroku Scheduler" >}},
-I can move accounts from their trial status
-to expired
-with a daily check.
-
-The following code is very close
-to what my `expire_trials` command looks like
-in my app.
-I've simplified things a bit,
-so that you can ignore the details
-that are specific to my service.
+Let's consider a powerful use case to see a command in action. When you initially start a Django app, all of your app's interaction will probably be through web pages. After all, you were trying to use Django to make a web app, right? What do you do when you need to do something that doesn't involve a browser?
+
+This kind of work for your app is often considered *background* work. Background work is a pretty deep topic and will often involve special background task software like {{< extlink "https://docs.celeryproject.org/en/stable/getting-started/introduction.html" "Celery" >}}. When your app is at an early stage, Celery or similar software can be overkill and far more than you need.
+
+A simpler alternative for some background tasks could be a command paired with a scheduling tool like {{< extlink "https://en.wikipedia.org/wiki/Cron" "cron" >}}.
+
+On one of my projects, I offer free trials for accounts. After 60 days, the free trial ends and users either need to pay for the service or discontinue using it. By using a command and pairing it with the {{< extlink "https://devcenter.heroku.com/articles/scheduler" "Heroku Scheduler" >}}, I can move accounts from their trial status to expired with a daily check.
+
+The following code is very close to what my `expire_trials` command looks like in my app. I've simplified things a bit, so that you can ignore the details that are specific to my service:
```python
# application/management/commands/expire_trials.py
@@ -370,35 +226,13 @@ class Command(BaseCommand):
)
```
-I configured the scheduler
-to run `python manage.py expire_trials`
-every day
-in the early morning.
-The command checks the current time
-and looks for `Account` records
-in the trialing state
-that were created before the cutoff time.
-From that query set,
-the affected accounts are set to the expired account state.
-
-How can you test this command?
-There are a couple of approaches you can take
-when testing a command.
-
-If you need to simulate calling the command
-with command line arguments,
-then you can use `call_command`
-from `django.core.management`.
-Since the example command doesn't require arguments,
-I didn't take that approach.
-
-Generally, my preference is to create a command object
-and invoke the `handle` method directly.
-In my example above,
-you can see that the command uses `self.stdout`
-instead of calling `print`.
-Django does this so that you could check your output
-if desired.
+I configured the scheduler to run `python manage.py expire_trials` every day in the early morning. The command checks the current time and looks for `Account` records in the trialing state that were created before the cutoff time. From that query set, the affected accounts are set to the expired account state.
+
+How can you test this command? There are a couple of approaches you can take when testing a command.
+
+If you need to simulate calling the command with command line arguments, then you can use `call_command` from `django.core.management`. Since the example command doesn't require arguments, I didn't take that approach.
+
+Generally, my preference is to create a command object and invoke the `handle` method directly. In my example above, you can see that the command uses `self.stdout` instead of calling `print`. Django does this so that you could check your output if desired.
Here is a test for this command:
@@ -429,34 +263,13 @@ def test_expires_trials():
assert "Expired 1 trial(s)" in stdout.getvalue()
```
-In this test,
-I constructed a command instance
-and checked the account state
-after the command invocation.
-Also,
-observe that the `StringIO` instance is injected
-into the `Command` constructor.
-By building the command this way,
-checking the output becomes a very achievable task
-via the `getvalue` method.
-
-Overall,
-this scheme of making a command
-and running it on a schedule
-avoids all the work
-of setting up a background worker process.
-I've been extremely satisfied
-with how this technique has worked
-for me,
-and I think it's a great pattern
-when your app doesn't have to do a lot
-of complex background processing.
+In this test, I constructed a command instance and checked the account state after the command invocation. Also, observe that the `StringIO` instance is injected into the `Command` constructor. By building the command this way, checking the output becomes a very achievable task via the `getvalue` method.
+
+Overall, this scheme of making a command and running it on a schedule avoids all the work of setting up a background worker process. I've been extremely satisfied with how this technique has worked for me, and I think it's a great pattern when your app doesn't have to do a lot of complex background processing.
## Useful Commands
-Django is full of
-{{< extlink "https://docs.djangoproject.com/en/4.1/ref/django-admin/" "useful commands" >}}
-that you can use for all kinds of purposes.
+Django is full of {{< extlink "https://docs.djangoproject.com/en/4.1/ref/django-admin/" "useful commands" >}} that you can use for all kinds of purposes.
{{< web >}}
Thus far in this series,
{{< /web >}}
@@ -471,48 +284,26 @@ we've discussed a bunch of them, including:
* `makemigrations` - Makes new migration files based on model changes.
* `migrate` - Runs any unapplied migrations to your database.
* `runserver` - Runs a development web server to check your app.
-* `shell` - Starts a Django shell that allows you to use Django code
- on the command line.
+* `shell` - Starts a Django shell that allows you to use Django code on the command line.
* `startapp` - Makes a new Django app from a template.
* `startproject` - Makes a new Django project from a template.
* `test` - Executes tests that check the validity of your app.
-Here is a sampling of other commands
-that I find useful
-when working with Django projects.
+Here is a sampling of other commands that I find useful when working with Django projects.
### `dbshell`
-The `dbshell` command starts a different kind of shell.
-The shell is a database program
-that will connect to the same database
-that your Django app uses.
-This shell will vary based on your choice of database.
+The `dbshell` command starts a different kind of shell. The shell is a database program that will connect to the same database that your Django app uses. This shell will vary based on your choice of database.
-For instance,
-when using PostgreSQL,
-`./manage.py dbshell`
+For instance, when using PostgreSQL, `./manage.py dbshell`
will start `{{< extlink "https://www.postgresql.org/docs/current/app-psql.html" "psql" >}}`.
-From this shell,
-you can execute SQL statements directly
-to inspect the state
-of your database.
-I don't reach for this command often,
-but I find it very useful to connect
-to my database without having to remember database credentials.
+From this shell, you can execute SQL statements directly to inspect the state of your database. I don't reach for this command often, but I find it very useful to connect to my database without having to remember database credentials.
### `showmigrations`
-The `showmigrations` command has a simple job.
-The command shows all the migrations
-for each Django app
-in your project.
-Next to each migration is an indicator
-of whether the migration is applied
-to your database.
+The `showmigrations` command has a simple job. The command shows all the migrations for each Django app in your project. Next to each migration is an indicator of whether the migration is applied to your database.
-Here is an example of the `users` app
-from one of my Django projects:
+Here is an example of the `users` app from one of my Django projects:
```bash
$ ./manage.py showmigrations users
@@ -522,40 +313,19 @@ users
[ ] 0003_profile
```
-In my real project,
-I've applied all the migrations,
-but for this example,
-I'm showing the third migration
-as it would appear
-if the migration wasn't applied yet.
+In my real project, I've applied all the migrations, but for this example, I'm showing the third migration as it would appear if the migration wasn't applied yet.
-`showmigrations` is a good way to show the state
-of your database
-from Django's point of view.
+`showmigrations` is a good way to show the state of your database from Django's point of view.
### `sqlmigrate`
-The `sqlmigrate` command is *very* handy.
-The command will show you what SQL statements Django would run
-for an individual migration file.
+The `sqlmigrate` command is *very* handy. The command will show you what SQL statements Django would run for an individual migration file.
-Let's see an example.
-In Django 3.1,
-the team changed the `AbstractUser` model
-so that the `first_name` field could have a maximum length
-of 150 characters.
-Anyone using the `AbstractUser` model
-(which includes me)
-had to generate a migration
-to apply that change.
+Let's see an example. In Django 3.1, the team changed the `AbstractUser` model so that the `first_name` field could have a maximum length of 150 characters. Anyone using the `AbstractUser` model (which includes me) had to generate a migration to apply that change.
-From my `showmigrations` output above,
-you can see that the second migration
-of my `users` app applied this particular framework change.
+From my `showmigrations` output above, you can see that the second migration of my `users` app applied this particular framework change.
-To see the Postgres SQL statements
-that made the change,
-I can run:
+To see the Postgres SQL statements that made the change, I can run:
```bash
$ ./manage.py sqlmigrate users 0002
@@ -567,102 +337,42 @@ ALTER TABLE "users_user" ALTER COLUMN "first_name" TYPE varchar(150);
COMMIT;
```
-From this,
-we can tell that Postgres executed an `ALTER COLUMN`
-{{< extlink "https://en.wikipedia.org/wiki/Data_definition_language" "DDL" >}}
-statement
-to modify the length
-of the `first_name` field.
+From this, we can tell that Postgres executed an `ALTER COLUMN` {{< extlink "https://en.wikipedia.org/wiki/Data_definition_language" "DDL" >}} statement to modify the length of the `first_name` field.
### `squashmigrations`
-Django migrations are a stack of separate database changes
-that produce a final desired schema state
-in your database.
-Over time,
-your Django apps will accumulate migration files,
-but those files have a shelf life.
-The `squashmigrations` command is designed
-to let you tidy up an app's set
-of migration files.
-
-By running `squashmigrations`,
-you can condense an app's migrations
-into a significantly smaller number.
-The reduced migrations can accurately represent your database schema,
-and make it easier to reason about what changes happened
-in the app's history.
-As a side benefit,
-migration squashing can make Django's migration handling faster
-because Django gets to process fewer files.
+Django migrations are a stack of separate database changes that produce a final desired schema state in your database. Over time, your Django apps will accumulate migration files, but those files have a shelf life. The `squashmigrations` command is designed to let you tidy up an app's set of migration files.
+
+By running `squashmigrations`, you can condense an app's migrations into a significantly smaller number. The reduced migrations can accurately represent your database schema, and make it easier to reason about what changes happened in the app's history. As a side benefit, migration squashing can make Django's migration handling faster because Django gets to process fewer files.
## Even More Useful Commands
-The commands above come with the standard Django install.
-Adding in third-party libraries gives you access
-to even more cool stuff to help with your project development!
+The commands above come with the standard Django install. Adding in third-party libraries gives you access to even more cool stuff to help with your project development!
-A package that I often reach for
-with my Django projects is the
-{{< extlink "https://django-extensions.readthedocs.io/en/latest/index.html" "django-extensions" >}}
-package.
-This package is full of goodies,
-including some great optional commands
-that you can use!
+A package that I often reach for with my Django projects is the {{< extlink "https://django-extensions.readthedocs.io/en/latest/index.html" "django-extensions" >}} package. This package is full of goodies, including some great optional commands that you can use!
A couple of my favorites include:
### `shell_plus`
-How often do you fire up a Django shell,
-import a model,
-then do some ORM queries
-to see the current state of the database?
-This is something I do *quite* often.
-
-The `shell_plus` command is like the regular shell,
-but the command will import all your models *automatically*.
-For the five extra characters
-of `_plus`,
-you can save your fingers a lot of typing
-to import your models
-and get directly to whatever you needed the shell for.
-
-The command will also import some commonly used Django functions
-and features
-like `reverse`, `settings,` `timezone`, and more.
-
-Also,
-if you have installed a separate REPL like
-{{< extlink "https://ipython.org/" "IPython" >}},
-`shell_plus` will attempt to use the alternate REPL instead
-of the default version that comes with Python.
+How often do you fire up a Django shell, import a model, then do some ORM queries to see the current state of the database? This is something I do *quite* often.
+
+The `shell_plus` command is like the regular shell, but the command will import all your models *automatically*. For the five extra characters of `_plus`, you can save your fingers a lot of typing to import your models and get directly to whatever you needed the shell for.
+
+The command will also import some commonly used Django functions and features like `reverse`, `settings,` `timezone`, and more.
+
+Also, if you have installed a separate REPL like {{< extlink "https://ipython.org/" "IPython" >}}, `shell_plus` will attempt to use the alternate REPL instead of the default version that comes with Python.
### `graph_models`
-When I'm live streaming my side projects
-on {{< extlink "https://www.youtube.com/c/MattLayman" "my YouTube channel" >}},
-I will often want to show the model relationships
-of my Django project.
-With the `graph_models` command,
-I can create an image of all my models
-and how those models relate to each other
-(using UML syntax).
-This is a great way to:
+When I'm live streaming my side projects on {{< extlink "https://www.youtube.com/c/MattLayman" "my YouTube channel" >}}, I will often want to show the model relationships of my Django project. With the `graph_models` command, I can create an image of all my models and how those models relate to each other (using UML syntax). This is a great way to:
* Remind myself of the data modeling choices in my apps.
* Orient others to what I'm doing with my project.
-This particular command requires some extra setup
-to install the right tools
-to create images,
-but the setup is manageable
-and the results are worth it.
+This particular command requires some extra setup to install the right tools to create images, but the setup is manageable and the results are worth it.
-Aside from `shell_plus` and `graph_models`,
-there are 20 other commands that you can use
-that may be very useful to you.
-You should definitely check out django-extensions.
+Aside from `shell_plus` and `graph_models`, there are 20 other commands that you can use that may be very useful to you. You should definitely check out django-extensions.
## Summary
@@ -673,6 +383,7 @@ In this article,
In this chapter,
{{< /book >}}
you saw Django commands.
+
We covered:
* Why commands exist in the Django framework
@@ -687,6 +398,7 @@ In the next article,
In the next chapter,
{{< /book >}}
we're going to look into performance.
+
You'll learn about:
* How Django sites get slow
@@ -694,15 +406,6 @@ You'll learn about:
* How to apply caching to save processing
{{< web >}}
-If you'd like to follow along
-with the series,
-please feel free to sign up
-for my newsletter
-where I announce all of my new content.
-If you have other questions,
-you can reach me online
-on Twitter
-where I am
-{{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
+If you'd like to follow along with the series, please feel free to sign up for my newsletter where I announce all of my new content. If you have other questions, you can reach me online on Twitter where I am {{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
{{< /web >}}
From bda72a167a7e312677aa2a4a17526651a92f1a8b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nazar=C3=A9=20da=20Piedade?=
Date: Fri, 5 May 2023 11:20:20 +0100
Subject: [PATCH 19/30] chore: format
`understand-django/2022-01-19-go-fast.pt.md`
---
.../2022-01-19-go-fast.pt.md | 1056 +++--------------
1 file changed, 173 insertions(+), 883 deletions(-)
diff --git a/content/understand-django/2022-01-19-go-fast.pt.md b/content/understand-django/2022-01-19-go-fast.pt.md
index 41c51e03..0077d3f3 100644
--- a/content/understand-django/2022-01-19-go-fast.pt.md
+++ b/content/understand-django/2022-01-19-go-fast.pt.md
@@ -1,15 +1,7 @@
---
title: "Go Fast With Django"
description: >-
- How do you make your Django app fast?
- You measure what is slow,
- scale your system when necessary,
- and use a combination
- of fast database queries
- and strategic caching.
- In this Understand Django article,
- we'll explore those topics and more
- to help you get a performant Django app.
+ How do you make your Django app fast? You measure what is slow, scale your system when necessary, and use a combination of fast database queries and strategic caching. In this Understand Django article, we'll explore those topics and more to help you get a performant Django app.
image: img/django.png
type: post
categories:
@@ -26,15 +18,12 @@ series: "Understand Django"
In the last
{{< web >}}
-[Understand Django]({{< ref "/understand-django/_index.md" >}})
-article,
+[Understand Django]({{< ref "/understand-django/_index.pt.md" >}}) article,
{{< /web >}}
{{< book >}}
chapter,
{{< /book >}}
-we learned about commands.
-Commands are the way to execute scripts
-that interact with your Django app.
+we learned about commands. Commands are the way to execute scripts that interact with your Django app.
{{< web >}}
With this article,
@@ -42,11 +31,9 @@ With this article,
{{< book >}}
With this chapter,
{{< /book >}}
-we're going to dig into performance.
-How do you make your Django site faster?
-Keep reading to find out.
+we're going to dig into performance. How do you make your Django site faster? Keep reading to find out.
-{{< understand-django-series "performance" >}}
+{{< understand-django-series-pt "performance" >}}
## Theory Of Performance
@@ -63,63 +50,28 @@ There are two ways to make a website faster:
* Do **less** work.
{{< /book >}}
-How we do more work *and* less work depends
-on the type of work
-that we're trying to address on the site.
-
-When we're talking about doing more work,
-what I'm really saying is that we often need
-to increase the ***throughput***
-of a site.
-Throughput is a measure of work over time.
-By increasing throughput,
-a site can serve more users concurrently.
-
-A very natural throughput measure
-on a Django site is *requests per second*.
-A page view on a site could contain multiple requests,
-so requests per second isn't a perfect analog
-to how many users your site can handle,
-but it's still a useful measure
-to help you reason about site performance.
-
-On the flip side of doing more work,
-what does it mean to do less work
-to improve performance?
+How we do more work *and* less work depends on the type of work that we're trying to address on the site.
+
+When we're talking about doing more work, what I'm really saying is that we often need to increase the ***throughput*** of a site. Throughput is a measure of work over time. By increasing throughput, a site can serve more users concurrently.
+
+A very natural throughput measure on a Django site is *requests per second*. A page view on a site could contain multiple requests, so requests per second isn't a perfect analog to how many users your site can handle, but it's still a useful measure to help you reason about site performance.
+
+On the flip side of doing more work, what does it mean to do less work to improve performance?
> The fastest code is no code.
-Every line of code that you write must be processed
-by a computer when it runs.
-If your code is inefficient
-or if too much is running,
-that will naturally mean
-that it will take longer to produce a final result.
-
-The time from when an input happens
-to when an output is received is called ***latency***.
-If a user clicks on a link
-on your site,
-how long does it take for them
-to get a response?
-That time delay is latency.
-
-Less work doesn't just mean
-that you're running too much code!
-There are a lot of factors
-that might contribute
-to latency,
-some of them are easier to optimize than others.
+Every line of code that you write must be processed by a computer when it runs. If your code is inefficient or if too much is running, that will naturally mean that it will take longer to produce a final result.
+
+The time from when an input happens to when an output is received is called ***latency***. If a user clicks on a link on your site, how long does it take for them to get a response? That time delay is latency.
+
+Less work doesn't just mean that you're running too much code! There are a lot of factors that might contribute to latency, some of them are easier to optimize than others.
* Inefficient code - mistakes in development can make a computer slower than necessary
* Data size - sending more data requires more effort to deliver to users
* Geographic location - the speed of light is a real limit on network communication
* and more!
-If you can reduce latency
-on your site,
-you can improve the experience
-of the people using the site.
+If you can reduce latency on your site, you can improve the experience of the people using the site.
{{< web >}}
In the rest of this article,
@@ -127,48 +79,20 @@ In the rest of this article,
{{< book >}}
In the rest of this chapter,
{{< /book >}}
-you'll learn how you can do both more and less work
-to make a better site
-that will benefit your users.
+you'll learn how you can do both more and less work to make a better site that will benefit your users.
## Measure First
-Before optimizing,
-we have to recognize what kind of work
-is impacting an app.
-In other words,
-*what is the resource constraint
-that is preventing an app
-from performing better?*
-
-Measuring applications,
-especially those that are running
-with live traffic,
-can be a tricky endeavor.
-I think we can look at an app
-from a zoomed out macro level
-or a zoomed in point of view.
-
-I would start my analysis
-from inspecting the system overall
-for patterns.
-Broadly,
-you will find that performance tends
-to fall into a couple of major bottleneck categories:
+Before optimizing, we have to recognize what kind of work is impacting an app. In other words, *what is the resource constraint that is preventing an app from performing better?*
+
+Measuring applications, especially those that are running with live traffic, can be a tricky endeavor. I think we can look at an app from a zoomed out macro level or a zoomed in point of view.
+
+I would start my analysis from inspecting the system overall for patterns. Broadly, you will find that performance tends to fall into a couple of major bottleneck categories:
* I/O bound
* CPU bound
-*I/O bound* means that the system is limited
-(that's the "bound" part)
-by the inputs and outputs
-of the system.
-Since that's still a really vague statement,
-let's make it more concrete.
-An I/O bound system is one
-that is waiting
-for work to be available.
-Classic examples include:
+*I/O bound* means that the system is limited (that's the "bound" part) by the inputs and outputs of the system. Since that's still a really vague statement, let's make it more concrete. An I/O bound system is one that is waiting for work to be available. Classic examples include:
* waiting for responses from a database
* waiting for content from a file system
@@ -177,229 +101,60 @@ Classic examples include:
Optimizing an I/O bound system is all about minimizing those waiting moments.
-Conversely,
-*CPU bound* systems are systems
-that are drowning in immediate work
-to calculate.
-The computer's **C**entral **P**rocessing **U**nit can't keep up
-with all that it's being asked to do.
-Classic examples of CPU bound work are:
+Conversely, *CPU bound* systems are systems that are drowning in immediate work to calculate. The computer's **C**entral **P**rocessing **U**nit can't keep up with all that it's being asked to do. Classic examples of CPU bound work are:
* Data science computations for machine learning
* Image processing and rendering
* Test suite execution
-Optimizing a CPU bound system focuses heavily
-on making calculations faster and cheaper.
-
-Since we understand
-that an application
-that is underperforming
-is likely to be I/O bound or CPU bound,
-we can start to look
-for these patterns
-within the system.
-The easiest initial signal to observe is the CPU load.
-If the processors
-on your production machines
-are running at very high CPU utilization,
-then it's a pretty clear indicator
-that your app is CPU bound.
-In my estimation,
-you'll rarely see this
-for web applications.
-**Most underperforming web applications are likely to be I/O bound.**
-
-The reason that web apps are often I/O bound has to do
-with their typical function.
-Most apps are fetching data from a database
-and displaying it to a user.
-There isn't a massive amount of computation
-(comparatively to something like machine learning)
-that the app needs to do.
-Thus,
-your app is probably waiting around
-for data
-from the database.
-
-If you observe that the system is not CPU bound,
-then the next action is to dig deeper
-and find where the application is spending its time waiting.
-But how can we do this?
-Philosophically,
-you should now have an ok understanding
-of what to be looking for,
-but what tools can you use to accomplish the task
-of measurement?
-
-We need to rewind a bit.
-A moment ago,
-I also made an assumption
-that you know how to find the CPU load
-of your application.
-That may not be the case.
-Let's look at some tools
-that help you categorize
-where your app's resource bottleneck is.
-
-The easiest way to monitor basic resource information
-about your app
-including CPU, memory, disk usage, and more
-may come from your hosting vendor.
-My preferred hosting vendor, Heroku, displays all these kinds
-of metrics
-on a single page
-so I can assess system performance at a glance.
-Other vendors like Digital Ocean or AWS provide tools
-to help you see this information too.
-
-If your hosting vendor doesn't provide these tools,
-then you'll have to use other techniques.
-Presumably,
-if you're using a Virtual Private Server (VPS)
-for hosting,
-you have access to the server itself via ssh.
-On the server that's running your application,
-you can use a program like `top`.
-This is a classic program
-for checking which processes are using the "top" amount
-of resources.
-`top` will show the list of processes
-ordered by what is consuming the most CPU
-and will refresh the order of the list every second
-to provide a current snapshot in time.
-(**Tip**: use `q` to quit `top` after you start it.)
-
-While `top` is useful and gets the job done to learn about CPU usage,
-it's not exactly the friendliest tool out there.
-There are alternatives to `top`
-that may offer a better user experience.
-I personally find `top` sufficient,
-but I know `htop` is a popular alternative.
-
-If you don't have tools
-from your hosting provider
-and don't want to use ssh to log into a server,
-there are other options to consider.
-Broadly,
-this other category of tools is called Application Performance Monitoring (APM).
-APM tools are vendors
-that will monitor your application (go figure!)
-if you install the tool along with your app.
-These tools help show *both* CPU problems
-and I/O issues.
-Application performance is very important to businesses,
-so the software industry is full of vendors to choose from
-with a wide range of features.
-
-To see what these tools can be like for free,
-you might want to check out
-{{< extlink "https://www.datadoghq.com/" "Datadog" >}}
-which has a free tier
-(Datadog is not a sponsor,
-I've just used their service,
-enjoyed it,
-and know that it's free
-for a small number of servers).
-Other popular vendors include
-{{< extlink "https://scoutapm.com/" "Scout APM" >}} and
-{{< extlink "https://newrelic.com/" "New Relic" >}}.
-
-Finally,
-we've reached a point where you can diagnose the performance constraints
-on your application using a wide variety
-of tools or services.
-Let's see how to fix problems you may be experiencing!
+Optimizing a CPU bound system focuses heavily on making calculations faster and cheaper.
+
+Since we understand that an application that is underperforming is likely to be I/O bound or CPU bound, we can start to look for these patterns within the system. The easiest initial signal to observe is the CPU load. If the processors on your production machines are running at very high CPU utilization, then it's a pretty clear indicator that your app is CPU bound. In my estimation, you'll rarely see this for web applications. **Most underperforming web applications are likely to be I/O bound.**
+
+The reason that web apps are often I/O bound has to do with their typical function. Most apps are fetching data from a database and displaying it to a user. There isn't a massive amount of computation (comparatively to something like machine learning) that the app needs to do. Thus, your app is probably waiting around for data from the database.
+
+If you observe that the system is not CPU bound, then the next action is to dig deeper and find where the application is spending its time waiting. But how can we do this? Philosophically, you should now have an ok understanding of what to be looking for, but what tools can you use to accomplish the task of measurement?
+
+We need to rewind a bit. A moment ago, I also made an assumption that you know how to find the CPU load of your application. That may not be the case. Let's look at some tools that help you categorize where your app's resource bottleneck is.
+
+The easiest way to monitor basic resource information about your app including CPU, memory, disk usage, and more may come from your hosting vendor. My preferred hosting vendor, Heroku, displays all these kinds of metrics on a single page so I can assess system performance at a glance. Other vendors like Digital Ocean or AWS provide tools to help you see this information too.
+
+If your hosting vendor doesn't provide these tools, then you'll have to use other techniques. Presumably, if you're using a Virtual Private Server (VPS) for hosting, you have access to the server itself via ssh. On the server that's running your application, you can use a program like `top`. This is a classic program for checking which processes are using the "top" amount of resources. `top` will show the list of processes ordered by what is consuming the most CPU and will refresh the order of the list every second to provide a current snapshot in time. (**Tip**: use `q` to quit `top` after you start it.)
+
+While `top` is useful and gets the job done to learn about CPU usage, it's not exactly the friendliest tool out there. There are alternatives to `top` that may offer a better user experience. I personally find `top` sufficient, but I know `htop` is a popular alternative.
+
+If you don't have tools from your hosting provider and don't want to use ssh to log into a server, there are other options to consider. Broadly, this other category of tools is called Application Performance Monitoring (APM). APM tools are vendors that will monitor your application (go figure!) if you install the tool along with your app. These tools help show *both* CPU problems and I/O issues. Application performance is very important to businesses, so the software industry is full of vendors to choose from with a wide range of features.
+
+To see what these tools can be like for free, you might want to check out {{< extlink "https://www.datadoghq.com/" "Datadog" >}} which has a free tier (Datadog is not a sponsor, I've just used their service, enjoyed it, and know that it's free for a small number of servers). Other popular vendors include {{< extlink "https://scoutapm.com/" "Scout APM" >}} and {{< extlink "https://newrelic.com/" "New Relic" >}}.
+
+Finally, we've reached a point where you can diagnose the performance constraints on your application using a wide variety of tools or services. Let's see how to fix problems you may be experiencing!
## Do More
-We can address throughput and do more
-by tuning a few different knobs.
+We can address throughput and do more by tuning a few different knobs.
-When thinking about doing more,
-try to think about the system
-in two different scaling dimensions:
+When thinking about doing more, try to think about the system in two different scaling dimensions:
* Horizontally
* Vertically
-Horizontal and vertical scaling are methods
-of describing *how* to do more
-in software systems.
-
-Let's relate this to a silly example
-to give you a good intuitive feel for scaling.
-Imagine that you need to move large bags
-of dirt
-(approximately 40 lbs / 18 kg per bag)
-to plant a huge garden.
-The job is to unload the hundreds of bags
-from a delivery truck
-to your imaginary back yard.
-You enlist the help
-of your friends
-to get the job done.
-
-One strategy is to get your *strongest* friends to help.
-Maybe there aren't as many of them,
-but their strength can make quick work
-of moving the bags.
-This is *vertical scaling*.
-The additional power of your friends allows them
-to move the bags more easily
-than someone with an average or weaker build.
-
-Another strategy is to get *lots* of friends to help.
-Maybe these friends can't move as many bags
-as the stronger ones,
-but many hands make light work.
-This is *horizontal scaling*.
-The increased number of people allows the group
-to move more bags
-because more individuals can do the work simultaneously.
+Horizontal and vertical scaling are methods of describing *how* to do more in software systems.
+
+Let's relate this to a silly example to give you a good intuitive feel for scaling. Imagine that you need to move large bags of dirt (approximately 40 lbs / 18 kg per bag) to plant a huge garden. The job is to unload the hundreds of bags from a delivery truck to your imaginary back yard. You enlist the help of your friends to get the job done.
+
+One strategy is to get your *strongest* friends to help. Maybe there aren't as many of them, but their strength can make quick work of moving the bags. This is *vertical scaling*. The additional power of your friends allows them to move the bags more easily than someone with an average or weaker build.
+
+Another strategy is to get *lots* of friends to help. Maybe these friends can't move as many bags as the stronger ones, but many hands make light work. This is *horizontal scaling*. The increased number of people allows the group to move more bags because more individuals can do the work simultaneously.
We can apply this same thinking to computer systems.
### Vertical Scaling
-To achieve vertical scaling,
-you would run your application on a more powerful computer.
-Cloud vendors give you all kinds of tools
-to do this.
-Picking a faster computer is naturally going to cost you more,
-so vendors make many options available
-(check out
-{{< extlink "https://aws.amazon.com/ec2/instance-types/" "this page from AWS" >}}
-to see the dizzying array of options).
-
-When should you think about vertical scaling?
-One natural case is when your application is CPU bound.
-If the processor is struggling
-to process the requests from an application,
-a faster processor may help.
-With a higher clock speed from a faster individual CPU,
-a computer will be able to process an individual request faster.
-
-Moving to a larger computer is typically considered vertical scaling,
-but it may be possible to have horizontal effects
-by moving to a larger computer
-because of how modern computers are designed.
-These days,
-larger computers typically come
-with a higher number of CPUs.
-Each individual CPU may be faster
-than a smaller computer configuration
-*and* there will be more CPUs
-on the single machine.
-Because of this characteristic,
-you will likely need to change your application configuration
-to take advantage of the additional power
-supplied by the extra CPU cores.
-While the traditional definition of vertical scaling
-(i.e., a faster individual CPU can do work quicker than a slower one) still applies,
-the line between vertical and horizontal scaling is somewhat blurred
-because of the multi-CPU core paradigm
-of modern CPUs.
+To achieve vertical scaling, you would run your application on a more powerful computer. Cloud vendors give you all kinds of tools to do this. Picking a faster computer is naturally going to cost you more, so vendors make many options available (check out {{< extlink "https://aws.amazon.com/ec2/instance-types/" "this page from AWS" >}} to see the dizzying array of options).
+
+When should you think about vertical scaling? One natural case is when your application is CPU bound. If the processor is struggling to process the requests from an application, a faster processor may help. With a higher clock speed from a faster individual CPU, a computer will be able to process an individual request faster.
+
+Moving to a larger computer is typically considered vertical scaling, but it may be possible to have horizontal effects by moving to a larger computer because of how modern computers are designed. These days, larger computers typically come with a higher number of CPUs. Each individual CPU may be faster than a smaller computer configuration *and* there will be more CPUs on the single machine. Because of this characteristic, you will likely need to change your application configuration to take advantage of the additional power supplied by the extra CPU cores. While the traditional definition of vertical scaling (i.e., a faster individual CPU can do work quicker than a slower one) still applies, the line between vertical and horizontal scaling is somewhat blurred because of the multi-CPU core paradigm of modern CPUs.
{{< web >}}
In the Understand Django deployment article,
@@ -407,213 +162,56 @@ In the Understand Django deployment article,
{{< book >}}
In the deployment chapter,
{{< /book >}}
-we discussed Gunicorn's `--workers` flag.
-Recall that Python application servers
-like Gunicorn work
-by creating a main process
-and a set of worker processes.
-The main process will distribute incoming network connections
-to the worker processes to handle the actual traffic
-on your website.
-If you vertically scale the server machine
-from a size that has 1 CPU
-to a machine that has 2, 4, or more CPUs,
-and you don't change the number of workers,
-then you'll waste available CPU capacity
-and won't see most of the benefits
-from the upgrade in server size.
-
-If modern vertical scaling uses more CPUs
-when moving to a bigger machine,
-then what is horizontal scaling?
-The difference is primarily in the number
-of computers needed
-to do the scaling.
-Vertical scaling changes a single machine
-to achieve more throughput.
-Horizontal scaling pulls multiple machines
-into the equation.
+we discussed Gunicorn's `--workers` flag. Recall that Python application servers like Gunicorn work by creating a main process and a set of worker processes. The main process will distribute incoming network connections to the worker processes to handle the actual traffic on your website. If you vertically scale the server machine from a size that has 1 CPU to a machine that has 2, 4, or more CPUs, and you don't change the number of workers, then you'll waste available CPU capacity and won't see most of the benefits from the upgrade in server size.
+
+If modern vertical scaling uses more CPUs when moving to a bigger machine, then what is horizontal scaling? The difference is primarily in the number of computers needed to do the scaling. Vertical scaling changes a single machine to achieve more throughput. Horizontal scaling pulls multiple machines into the equation.
### Horizontal Scaling
-Conceptually,
-how does horizontal scaling work?
-With the vertical scaling model,
-you can see a clear connection
-between users making a request
-to your website's domain
-and a single machine handling those requests
-(i.e., the main process from your application server
-distributes requests).
-With the horizontal model,
-we're now discussing multiple computers.
-How does a single domain name handle routing
-to multiple computers?
-With more computers!
-
-Like the main process
-that distributes requests,
-we need a central hub
-that is able to route traffic
-to the different machines
-in your horizontally scaled system.
-This hub is usually called a **load balancer**.
-A load balancer can be used
-for multiple things.
-I see load balancers used primarily to:
-
-* route traffic to the different application servers
- in a system
+Conceptually, how does horizontal scaling work? With the vertical scaling model, you can see a clear connection between users making a request to your website's domain and a single machine handling those requests (i.e., the main process from your application server distributes requests). With the horizontal model, we're now discussing multiple computers. How does a single domain name handle routing to multiple computers? With more computers!
+
+Like the main process that distributes requests, we need a central hub that is able to route traffic to the different machines in your horizontally scaled system. This hub is usually called a **load balancer**. A load balancer can be used for multiple things. I see load balancers used primarily to:
+
+* route traffic to the different application servers in a system
* handle the TLS certificate management that makes HTTPS possible
-Since the load balancer doesn't do most
-of the actual work
-of processing a request,
-your system can increase its throughput
-by increasing the number
-of application servers.
-In this setup,
-each application server "thinks"
-that it is the main server that's handling requests.
-The load balancer behaves like a client
-that's making requests on behalf
-of the actual user.
-This kind of configuration is called a proxy setup.
-
-If you want to learn more about horizontal scaling
-with a load balancer,
-then I suggest you check out
-{{< extlink "https://www.nginx.com/" "Nginx" >}} (pronounced "engine X"),
-{{< extlink "http://www.haproxy.org/" "HAProxy" >}}
-(which stands for "high availability proxy"),
-or {{< extlink "https://docs.aws.amazon.com/elasticloadbalancing/latest/application/introduction.html" "AWS ALBs" >}}
-(for "application load balancer").
-These tools are commonly used
-and have a reputation for being strong load balancers.
+Since the load balancer doesn't do most of the actual work of processing a request, your system can increase its throughput by increasing the number of application servers. In this setup, each application server "thinks" that it is the main server that's handling requests. The load balancer behaves like a client that's making requests on behalf of the actual user. This kind of configuration is called a proxy setup.
+
+If you want to learn more about horizontal scaling with a load balancer, then I suggest you check out {{< extlink "https://www.nginx.com/" "Nginx" >}} (pronounced "engine X"), {{< extlink "http://www.haproxy.org/" "HAProxy" >}} (which stands for "high availability proxy"), or {{< extlink "https://docs.aws.amazon.com/elasticloadbalancing/latest/application/introduction.html" "AWS ALBs" >}} (for "application load balancer"). These tools are commonly used and have a reputation for being strong load balancers.
### What's Better?
*What are the tradeoffs between horizontal scaling and vertical scaling?*
-When you add more pieces to a system,
-you're increasing the complexity of the system.
-Thus,
-vertical scaling can, at least initially, produce a design
-with lower operational complexity.
-Personally,
-if I ran a service
-on some VPS like Digital Ocean or AWS,
-I would probably reach for vertical scaling first.
-A bigger machine would allow me to use a higher number
-of concurrent worker processes
-to increase throughput,
-and I would avoid the complexity
-of deploying multiple application servers.
-
-In reality,
-I run my side projects on a Platform as a Service, Heroku.
-With my choice of Heroku,
-the service already includes a load balancer by default.
-This means that I can trivially scale horizontally
-by changing a setting in Heroku
-that will start multiple application servers.
-
-While vertical scaling may be a good fit
-if you don't have an existing load balancer,
-that scaling path does have downsides to consider.
-
-First,
-in a vertically scaled world,
-downtime on your server could mean downtime
-*for your service*.
-Whether a site is reachable or not reachable is called "availability"
-by the software industry.
-If your entire site is tied to a large vertically scaled server,
-then it can act as a single point of failure
-if there is a problem.
-
-Secondly,
-a vertically scaled service may potentially have more cost
-for you.
-In my experience,
-most websites have high and low periods
-of usage throughout the day.
-For instance,
-my current employer is a US healthcare company
-that provides telemedicine visits
-for people that need to speak
-with a doctor virtually.
-When it's the middle of the night
-in the US,
-the site utilization is naturally lower
-as most people are sleeping.
-
-One common cost optimization is to use fewer computing resources
-during periods of lower utilization.
-On a vertically scaled service,
-it is harder to change computer sizes quickly.
-Thus,
-that computing resource's usage is relatively fixed,
-even if no one is using your service.
-In contrast,
-a horizontally scaled service can be configured
-to use "auto-scaling."
-
-Auto-scaling is the idea
-that the infrastructure can be resized dynamically,
-depending on the use of the site.
-During periods of high activity,
-more computers can be added automatically
-to join the load balancer distribution
-and handle additional load.
-When activity dies down,
-these extra machines can be removed from use.
-This cost saving technique helps ensure
-that your system is only using what it needs.
-
-The truth is that if your system reaches a large enough size and scale,
-then picking horizontal or vertical scaling is a false choice.
-As a system matures and grows,
-you may need to have a mix of the two scaling types,
-so that your service has the characteristics
-that you need
-(like availability).
-
-I hope that I've helped equip you
-with some mental modeling tools.
-With these tools,
-you should have some idea of how to handle more traffic
-when your site becomes wildly popular. 😄
-
-In this section,
-we focused on increasing throughput
-by changing your service's infrastructure
-to handle more load.
-Now let's shift from the macro point of view
-to the micro view
-and talk about how to improve throughput
-by doing less.
+When you add more pieces to a system, you're increasing the complexity of the system. Thus, vertical scaling can, at least initially, produce a design with lower operational complexity. Personally, if I ran a service on some VPS like Digital Ocean or AWS, I would probably reach for vertical scaling first. A bigger machine would allow me to use a higher number of concurrent worker processes to increase throughput, and I would avoid the complexity of deploying multiple application servers.
+
+In reality, I run my side projects on a Platform as a Service, Heroku. With my choice of Heroku, the service already includes a load balancer by default. This means that I can trivially scale horizontally by changing a setting in Heroku that will start multiple application servers.
+
+While vertical scaling may be a good fit if you don't have an existing load balancer, that scaling path does have downsides to consider.
+
+First, in a vertically scaled world, downtime on your server could mean downtime *for your service*. Whether a site is reachable or not reachable is called "availability" by the software industry. If your entire site is tied to a large vertically scaled server, then it can act as a single point of failure if there is a problem.
+
+Secondly, a vertically scaled service may potentially have more cost for you. In my experience, most websites have high and low periods of usage throughout the day. For instance, my current employer is a US healthcare company that provides telemedicine visits for people that need to speak with a doctor virtually. When it's the middle of the night in the US, the site utilization is naturally lower as most people are sleeping.
+
+One common cost optimization is to use fewer computing resources during periods of lower utilization. On a vertically scaled service, it is harder to change computer sizes quickly. Thus, that computing resource's usage is relatively fixed, even if no one is using your service. In contrast, a horizontally scaled service can be configured to use "auto-scaling."
+
+Auto-scaling is the idea that the infrastructure can be resized dynamically, depending on the use of the site. During periods of high activity, more computers can be added automatically to join the load balancer distribution and handle additional load. When activity dies down, these extra machines can be removed from use. This cost saving technique helps ensure that your system is only using what it needs.
+
+The truth is that if your system reaches a large enough size and scale, then picking horizontal or vertical scaling is a false choice. As a system matures and grows, you may need to have a mix of the two scaling types, so that your service has the characteristics that you need (like availability).
+
+I hope that I've helped equip you with some mental modeling tools. With these tools, you should have some idea of how to handle more traffic when your site becomes wildly popular. 😄
+
+In this section, we focused on increasing throughput by changing your service's infrastructure to handle more load. Now let's shift from the macro point of view to the micro view and talk about how to improve throughput by doing less.
## Do Less
-*How do you make your Django site do less work?*
-We should always measure,
-but since I believe most websites are I/O bound,
-let's focus on techniques
-to improve in that dimension.
+*How do you make your Django site do less work?* We should always measure, but since I believe most websites are I/O bound, let's focus on techniques to improve in that dimension.
### Optimizing Database Queries
-The most common performance problem
-that I've encountered
-with Django applications
-is the N+1 query bug
-(some people will describe it as the 1+N query bug
-for reasons that may become evident in a moment).
+The most common performance problem that I've encountered with Django applications is the N+1 query bug (some people will describe it as the 1+N query bug for reasons that may become evident in a moment).
-The N+1 bug occurs when your application code calls the database
-in a loop.
-How many queries are in this made up example?
+The N+1 bug occurs when your application code calls the database in a loop. How many queries are in this made up example?:
```python
from application.models import Movie
@@ -623,28 +221,9 @@ for movie in movies:
print(movie.director.name)
```
-It's a bit of a trick question because
-you might have a custom manager (i.e., `objects`),
-but,
-in the simplest scenario,
-there is one query to fetch the movies,
-and one query for each director.
-
-The reason for this behavior is that Django does a lazy evaluation
-of the movies `QuerySet`.
-The ORM is not aware
-that it needs to do a join on the movie and director tables
-to fetch all the data.
-The first query on the movie table occurs
-when the iteration happens
-in the Python `for` loop.
-When the `print` function tries to access the `director` foreign key,
-the ORM does not have the director information cached
-in Python memory
-for the query set.
-Django must then fetch the director data
-in another database query
-to display the director's name.
+It's a bit of a trick question because you might have a custom manager (i.e., `objects`), but, in the simplest scenario, there is one query to fetch the movies, and one query for each director.
+
+The reason for this behavior is that Django does a lazy evaluation of the movies `QuerySet`. The ORM is not aware that it needs to do a join on the movie and director tables to fetch all the data. The first query on the movie table occurs when the iteration happens in the Python `for` loop. When the `print` function tries to access the `director` foreign key, the ORM does not have the director information cached in Python memory for the query set. Django must then fetch the director data in another database query to display the director's name.
This adds up to:
@@ -653,18 +232,9 @@ This adds up to:
Hence the name, "N+1" query bug.
-The reason that this is so bad is because calling the database is way slower
-than accessing data in Python memory.
-Also, this problem gets worse
-if there are more rows to iterate over
-(i.e., more movies to process and, thus, more directors to fetch individually).
+The reason that this is so bad is because calling the database is way slower than accessing data in Python memory. Also, this problem gets worse if there are more rows to iterate over (i.e., more movies to process and, thus, more directors to fetch individually).
-The way to fix this issue is to hint to Django
-that the code is going to access data
-from the deeper relationship.
-We can do that by hinting to the ORM
-with `select_related`.
-Let's see the previous example with this change.
+The way to fix this issue is to hint to Django that the code is going to access data from the deeper relationship. We can do that by hinting to the ORM with `select_related`. Let's see the previous example with this change:
```python
from application.models import Movie
@@ -675,44 +245,13 @@ for movie in movies:
print(movie.director.name)
```
-In the reworked example,
-the ORM will "know" that it must fetch the director data.
-Because of this extra information,
-the framework will fetch from both the movie and director tables
-*in a single query*
-when the `for` loop iteration starts.
-
-Under the hood,
-Django performs a more complex `SELECT` query
-that includes a join
-on the two tables.
-The database sends back all the data at once
-and Django caches the data
-in Python memory.
-Now,
-when execution reaches the `print` line,
-the `director.name` attribute can pull from memory
-instead of needing to trigger another database query.
-
-The performance savings here can be massive,
-especially if your code works with a lot of database rows
-at once.
-
-While `select_related` is fantastic,
-it doesn't work for all scenarios.
-Other types of relationships
-like a many to many relationship can't be fetched
-in a single query.
-For those scenarios,
-you can reach for `prefetch_related`.
-With this method,
-Django will issue a smaller number of queries
-(usually 1 per table)
-and blend the results together in memory.
-In practice,
-`prefetch_related` operates very much like `select_related`
-in most circumstances.
-Check out the Django docs to understand more.
+In the reworked example, the ORM will "know" that it must fetch the director data. Because of this extra information, the framework will fetch from both the movie and director tables *in a single query* when the `for` loop iteration starts.
+
+Under the hood, Django performs a more complex `SELECT` query that includes a join on the two tables. The database sends back all the data at once and Django caches the data in Python memory. Now, when execution reaches the `print` line, the `director.name` attribute can pull from memory instead of needing to trigger another database query.
+
+The performance savings here can be massive, especially if your code works with a lot of database rows at once.
+
+While `select_related` is fantastic, it doesn't work for all scenarios. Other types of relationships like a many to many relationship can't be fetched in a single query. For those scenarios, you can reach for `prefetch_related`. With this method, Django will issue a smaller number of queries (usually 1 per table) and blend the results together in memory. In practice, `prefetch_related` operates very much like `select_related` in most circumstances. Check out the Django docs to understand more.
### Caching Expensive Work
@@ -722,55 +261,17 @@ If you know:
* is expensive to create, AND
* won't need to change
-then you're looking at work
-that is a very good candidate to cache.
-With caching,
-Django can save the results
-of some expensive operation
-into a very fast caching tool
-and restore those results later.
-
-A good example of this might be a news site.
-A news site is very "read heavy,"
-that is,
-users are more likely to use the site
-for viewing information
-than for writing and saving information
-to the site.
-A news site is also a good example
-because users will read the same article,
-and the content of that article is fixed in form.
-
-Django includes tools to make it simple
-to work with the cache
-to optimize content like our news site example.
-
-The simplest of these tools is the `cache_page` decorator.
-This decorator can cache the results
-of an entire Django view
-for a period of time.
-When a page doesn't have any personalization,
-this can be a quick and effective way
-to serve HTML results
-from a view.
-You can find this decorator
-in `django.views.decorators.cache`.
-
-You may need a lower level of granularity
-than a whole page.
-For instance,
-your site might have some kind of logged in user
-and a customized navigation bar
-with a profile picture or something similar.
-In that scenario,
-you can't really cache the whole page
-and serve that to multiple users,
-because other users would see the customized navigation bar
-of the first user who made the request.
-If this is the kind of situation you're in,
-then the `cache` template tag may be the best tool for you.
-
-Here's a template example of the `cache` tag in use.
+then you're looking at work that is a very good candidate to cache. With caching, Django can save the results of some expensive operation into a very fast caching tool and restore those results later.
+
+A good example of this might be a news site. A news site is very "read heavy," that is, users are more likely to use the site for viewing information than for writing and saving information to the site. A news site is also a good example because users will read the same article, and the content of that article is fixed in form.
+
+Django includes tools to make it simple to work with the cache to optimize content like our news site example.
+
+The simplest of these tools is the `cache_page` decorator. This decorator can cache the results of an entire Django view for a period of time. When a page doesn't have any personalization, this can be a quick and effective way to serve HTML results from a view. You can find this decorator in `django.views.decorators.cache`.
+
+You may need a lower level of granularity than a whole page. For instance, your site might have some kind of logged in user and a customized navigation bar with a profile picture or something similar. In that scenario, you can't really cache the whole page and serve that to multiple users, because other users would see the customized navigation bar of the first user who made the request. If this is the kind of situation you're in, then the `cache` template tag may be the best tool for you.
+
+Here's a template example of the `cache` tag in use:
{{< web >}}
```django
@@ -779,14 +280,9 @@ Here's a template example of the `cache` tag in use.
Hi {{ user.username }}, this part won't be cached.
{% cache 600 my_cache_key_name %}
- Everything inside of here will be cached.
- The first argument to `cache` is how long this should be cached
- in seconds. This cache fragment will cache for 10 minutes.
- Cached chunks need a key name to help the cache system
- find the right cache chunk.
-
- This cache example usage is a bit silly because this is static text
- and there is no expensive computation in this chunk.
+ Everything inside of here will be cached. The first argument to `cache` is how long this should be cached in seconds. This cache fragment will cache for 10 minutes. Cached chunks need a key name to help the cache system find the right cache chunk.
+
+ This cache example usage is a bit silly because this is static text and there is no expensive computation in this chunk.
{% endcache %}
```
{{< /web >}}
@@ -797,31 +293,16 @@ Hi {{ user.username }}, this part won't be cached.
Hi {{ user.username }}, this part won't be cached.
{% cache 600 my_cache_key_name %}
- Everything inside of here will be cached.
- The first argument to `cache` is how long this should be cached
- in seconds. This cache fragment will cache for 10 minutes.
- Cached chunks need a key name to help the cache system
- find the right cache chunk.
-
- This cache example usage is a bit silly because this is static text
- and there is no expensive computation in this chunk.
+ Everything inside of here will be cached. The first argument to `cache` is how long this should be cached in seconds. This cache fragment will cache for 10 minutes. Cached chunks need a key name to help the cache system find the right cache chunk.
+
+ This cache example usage is a bit silly because this is static text and there is no expensive computation in this chunk.
{% endcache %}
```
{{< /book >}}
-With this scheme,
-any expensive computation
-that your template does will be cached.
-*Be careful with this tag!*
-The tag is useful if computation happens during rendering,
-but if you're doing the evaluation and fetching *inside
-of your view*
-instead of at template rendering time,
-then you're unlikely to get the benefits that you want.
+With this scheme, any expensive computation that your template does will be cached. *Be careful with this tag!* The tag is useful if computation happens during rendering, but if you're doing the evaluation and fetching *inside of your view* instead of at template rendering time, then you're unlikely to get the benefits that you want.
-Finally,
-there is the option to use the cache interface directly.
-Here's the basic usage pattern:
+Finally, there is the option to use the cache interface directly. Here's the basic usage pattern:
```python
# application/views.py
@@ -843,73 +324,23 @@ def some_view(request):
...
```
-On the first request to this view,
-the `expensive_result` won't be in the cache,
-so the view will calculate the result
-and save it to the cache.
-On subsequent requests,
-the expensive result can be pulled
-from the cache.
-In this example,
-I'm using the default timeout
-for the cache,
-but you can control the timeout values
-when you need more control.
-The cache system has plenty
-of other cool features,
-so check it out in the docs.
-
-As fair warning,
-caching often requires more tools and configuration.
-Django works with very popular cache tools
-like Redis and Memcached,
-but you'll have to configure one of the tools
-on your own.
-The Django documentation will help you,
-but be prepared for more work on your part.
-
-Database optimization and caching are go-to techniques
-for optimization.
-When you're optimizing,
-how do you know that you're doing it right?
-What gains are you getting?
-Let's look at some tools next
-that will let you answer those questions.
+On the first request to this view, the `expensive_result` won't be in the cache, so the view will calculate the result and save it to the cache. On subsequent requests, the expensive result can be pulled from the cache. In this example, I'm using the default timeout for the cache, but you can control the timeout values when you need more control. The cache system has plenty of other cool features, so check it out in the docs.
+
+As fair warning, caching often requires more tools and configuration. Django works with very popular cache tools like Redis and Memcached, but you'll have to configure one of the tools on your own. The Django documentation will help you, but be prepared for more work on your part.
+
+Database optimization and caching are go-to techniques for optimization. When you're optimizing, how do you know that you're doing it right? What gains are you getting? Let's look at some tools next that will let you answer those questions.
## Tools To Measure Change
-We'll look at tools at an increasing level of complexity.
-This first tool is one
-that is massively useful
-while developing in Django.
-The other tools are more general purpose tools,
-but they still are worth knowing about,
-so that you'll know when to reach for them.
+We'll look at tools at an increasing level of complexity. This first tool is one that is massively useful while developing in Django. The other tools are more general purpose tools, but they still are worth knowing about, so that you'll know when to reach for them.
-Each of these tools helps you get real performance data.
-By measuring the before and after
-of your changes,
-you can learn if the changes are actually producing the gains
-that you expect or hope to achieve.
+Each of these tools helps you get real performance data. By measuring the before and after of your changes, you can learn if the changes are actually producing the gains that you expect or hope to achieve.
### Django Debug Toolbar
-The
-{{< extlink "https://django-debug-toolbar.readthedocs.io/en/latest/index.html" "Django Debug Toolbar" >}}
-is a critical tool
-that I add to my projects.
-The toolbar acts as an overlay
-on your site
-that opens to give you a tray
-of different categories
-of diagnostic information
-about your views.
-
-Each category of information is grouped
-into a "panel,"
-and you can select between the different panels
-to dig up information
-that will assist you while doing optimization work.
+The {{< extlink "https://django-debug-toolbar.readthedocs.io/en/latest/index.html" "Django Debug Toolbar" >}} is a critical tool that I add to my projects. The toolbar acts as an overlay on your site that opens to give you a tray of different categories of diagnostic information about your views.
+
+Each category of information is grouped into a "panel," and you can select between the different panels to dig up information that will assist you while doing optimization work.
You'll find panels like:
@@ -918,52 +349,17 @@ You'll find panels like:
* Request
* Time
-The SQL panel is where I spend nearly all
-of my time when optimizing.
-This panel will display all the queries
-that a page requests.
-For each query,
-you can find what code triggered the database query
-and you even get the exact SQL `SELECT`.
-You can also get an `EXPLAIN` about a query
-if you really need the gory details
-of what the database is doing.
-
-With a little bit of eye training,
-you'll learn to spot N+1 query bugs
-because you can see certain queries repeated over and over
-and "cascading" like a waterfall.
-
-I'll often test with the debug toolbar
-when I'm trying to sprinkle in `select_related`
-to visually confirm
-that I've reduced the query count on a page.
-The debug toolbar is open source
-and is a great free resource.
-The toolbar is totally worth the investment
-of configuring it
-for your next Django project.
+The SQL panel is where I spend nearly all of my time when optimizing. This panel will display all the queries that a page requests. For each query, you can find what code triggered the database query and you even get the exact SQL `SELECT`. You can also get an `EXPLAIN` about a query if you really need the gory details of what the database is doing.
+
+With a little bit of eye training, you'll learn to spot N+1 query bugs because you can see certain queries repeated over and over and "cascading" like a waterfall.
+
+I'll often test with the debug toolbar when I'm trying to sprinkle in `select_related` to visually confirm that I've reduced the query count on a page. The debug toolbar is open source and is a great free resource. The toolbar is totally worth the investment of configuring it for your next Django project.
### hey / ab
-There are two tools
-that are very similar
-that I use when I need to get a crude measure
-of the performance of a site.
-These tools are
-{{< extlink "https://github.com/rakyll/hey" "hey" >}}
-and
-{{< extlink "https://httpd.apache.org/docs/2.4/programs/ab.html" "ab" >}}
-(Apache Bench).
-Both of these tools are *load generators*
-that are meant to benchmark a site's basic performance characteristics.
-
-In practice,
-I prefer `hey`,
-but I mention `ab`
-because it is a well known tool in this space
-that you are likely to encounter
-if you research this load generator topic.
+There are two tools that are very similar that I use when I need to get a crude measure of the performance of a site. These tools are {{< extlink "https://github.com/rakyll/hey" "hey" >}} and {{< extlink "https://httpd.apache.org/docs/2.4/programs/ab.html" "ab" >}} (Apache Bench). Both of these tools are *load generators* that are meant to benchmark a site's basic performance characteristics.
+
+In practice, I prefer `hey`, but I mention `ab` because it is a well known tool in this space that you are likely to encounter if you research this load generator topic.
Operating this kind of tool is trivial:
@@ -971,72 +367,19 @@ Operating this kind of tool is trivial:
$ hey https://www.example.com
```
-In this example,
-hey will try to open up a large number
-of concurrent connections
-to the URL
-and make a bunch of requests.
-When the tool is done,
-it will report how many
-of the requests were successful
-and some related timing information
-and statistics.
-Using a load generator like this lets you synthesize traffic
-to learn how your site is going to perform.
-
-I'd suggest you be careful
-where you tell these tools to operate.
-If you're not careful,
-*you could cause a Denial of Service attack
-on your own machines.*
-The flood of requests might make your site unavailable
-to other users
-by consuming all your server's resources.
-Think twice before pointing this at your live site!
+In this example, hey will try to open up a large number of concurrent connections to the URL and make a bunch of requests. When the tool is done, it will report how many of the requests were successful and some related timing information and statistics. Using a load generator like this lets you synthesize traffic to learn how your site is going to perform.
+
+I'd suggest you be careful where you tell these tools to operate. If you're not careful, *you could cause a Denial of Service attack on your own machines.* The flood of requests might make your site unavailable to other users by consuming all your server's resources. Think twice before pointing this at your live site!
### Locust
-The previous load generator tools
-that I mentioned act as somewhat crude measurements
-because you're limited
-to testing a single URL
-at a time.
-What should you do if you need to simulate traffic
-that matches real user usage patterns?
-Enter
-{{< extlink "https://locust.io/" "Locust" >}}.
-Locust is not a tool that I would reach for casually,
-but it is super cool and worth knowing about.
-
-The goal of Locust is to do load testing
-on your site
-in a realistic way.
-This means that it's your job to model the expected behavior
-of your users
-in a machine understandable way.
-If you know your users well
-(and I hope you do),
-then you can imagine the flows
-that they might follow
-when using your site.
-
-In Locust,
-you codify the behavior patterns
-that you care about,
-then run Locust
-to simulate a large number of users
-that will act like you expect
-(with randomness to boot
-to really make the test like reality).
-
-Advanced load testing is something you may never need
-for your site,
-but it's pretty cool to know
-that Python has you covered
-if you need to understand performance
-and the limits
-of your site
-at that deep level.
+The previous load generator tools that I mentioned act as somewhat crude measurements because you're limited to testing a single URL at a time. What should you do if you need to simulate traffic that matches real user usage patterns? Enter {{< extlink "https://locust.io/" "Locust" >}}. Locust is not a tool that I would reach for casually, but it is super cool and worth knowing about.
+
+The goal of Locust is to do load testing on your site in a realistic way. This means that it's your job to model the expected behavior of your users in a machine understandable way. If you know your users well (and I hope you do), then you can imagine the flows that they might follow when using your site.
+
+In Locust, you codify the behavior patterns that you care about, then run Locust to simulate a large number of users that will act like you expect (with randomness to boot to really make the test like reality).
+
+Advanced load testing is something you may never need for your site, but it's pretty cool to know that Python has you covered if you need to understand performance and the limits of your site at that deep level.
### Application Performance Monitoring (APM)
@@ -1046,76 +389,31 @@ Earlier in this article,
{{< book >}}
Earlier in this chapter,
{{< /book >}}
-I mentioned that Application Performance Monitoring tools
-can show you CPU and memory utilization
-of your site.
-That's usually just the tip of the iceberg.
-
-An APM tool often goes far beyond hardware resource measurement.
-I like to think
-of APMs
-as a supercharged version
-of the debug toolbar.
-
-First, an APM is used on live sites typically.
-The tool will collect data about real requests.
-This lets you learn about the *real* performance problems
-on the site
-that affect *real* users.
-
-For instance,
-New Relic will collect data
-on slow requests into "traces."
-These traces are aggregated
-into a set to show you which pages
-on your site
-are the worst performers.
-You can drill into that list,
-view an individual trace,
-and investigate the problem.
-
-Maybe you've got an N+1 bug.
-Maybe one of your database tables is missing an index
-on an important field,
-and the database is scanning too many records
-during `SELECT` statements.
-These traces (or whatever they are called in other services) help you prioritize
-what to fix.
-
-In fact,
-an APM highlights the true value of measurement.
-If I can leave you with a parting thought about optimization,
-think about this:
-***optimize where it counts***.
-
-Here's a simple thought experiment to illustrate what I mean.
-You have an idealized system that does two things repeatedly:
+I mentioned that Application Performance Monitoring tools can show you CPU and memory utilization of your site. That's usually just the tip of the iceberg.
+
+An APM tool often goes far beyond hardware resource measurement. I like to think of APMs as a supercharged version of the debug toolbar.
+
+First, an APM is used on live sites typically. The tool will collect data about real requests. This lets you learn about the *real* performance problems on the site that affect *real* users.
+
+For instance, New Relic will collect data on slow requests into "traces." These traces are aggregated into a set to show you which pages on your site are the worst performers. You can drill into that list, view an individual trace, and investigate the problem.
+
+Maybe you've got an N+1 bug. Maybe one of your database tables is missing an index on an important field, and the database is scanning too many records during `SELECT` statements. These traces (or whatever they are called in other services) help you prioritize what to fix.
+
+In fact, an APM highlights the true value of measurement. If I can leave you with a parting thought about optimization, think about this: ***optimize where it counts***.
+
+Here's a simple thought experiment to illustrate what I mean. You have an idealized system that does two things repeatedly:
* One task (A) is 90% of all activity on the site.
* The other task (B) is the remaining 10%.
-If you have to pick a target to try to optimize
-because your system performance is inadequate,
-which one do you pick?
+If you have to pick a target to try to optimize because your system performance is inadequate, which one do you pick?
-Let's assume that you know an optimization for each type of task
-that could cause the task to execute in 50% of the time.
-If implementing each optimization is the same level of effort,
-then there is a clear winner as to which task you should optimize.
-You could either:
+Let's assume that you know an optimization for each type of task that could cause the task to execute in 50% of the time. If implementing each optimization is the same level of effort, then there is a clear winner as to which task you should optimize. You could either:
* Optimize A for 90% * 50% for a total system saving of 45%.
* Optimize B for 10% * 50% for a total system saving of 5%.
-In most circumstances,
-spend your optimization effort
-on the area that will have outsized impact
-(i.e., pick task A as much as you can).
-Sometimes the hard part is figuring out which task is A
-and which task is B.
-Monitoring tools like an APM can help you see
-where the worst offenders are
-so you can focus your limited time in the right spot.
+In most circumstances, spend your optimization effort on the area that will have outsized impact (i.e., pick task A as much as you can). Sometimes the hard part is figuring out which task is A and which task is B. Monitoring tools like an APM can help you see where the worst offenders are so you can focus your limited time in the right spot.
## Summary
@@ -1126,13 +424,13 @@ In this article,
In this chapter,
{{< /book >}}
we looked into making Django apps go fast.
+
We saw:
* A mental model for thinking about performance optimization
* Different types of performance bottlenecks
* How to get your system to do more by either horizontal or vertical scaling
-* How to get your app to do less work
- by optimizing database queries and caching
+* How to get your app to do less work by optimizing database queries and caching
* Tools to aid you in all of this optimization work
{{< web >}}
@@ -1142,6 +440,7 @@ In the next article,
In the next chapter,
{{< /book >}}
we'll look into security.
+
You'll learn about:
* How Django helps you be more secure with some of its design features
@@ -1149,15 +448,6 @@ You'll learn about:
* Fundamental things you should consider to help keep your site secure
{{< web >}}
-If you'd like to follow along
-with the series,
-please feel free to sign up
-for my newsletter
-where I announce all of my new content.
-If you have other questions,
-you can reach me online
-on Twitter
-where I am
-{{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
+If you'd like to follow along with the series, please feel free to sign up for my newsletter where I announce all of my new content. If you have other questions, you can reach me online on Twitter where I am {{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
{{< /web >}}
From 9dbfa2089427d6b243405281f5ad1d8b7b5d12e4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nazar=C3=A9=20da=20Piedade?=
Date: Fri, 5 May 2023 11:20:48 +0100
Subject: [PATCH 20/30] chore: format
`understand-django/2022-03-10-secure-apps.pt.md`
---
.../2022-03-10-secure-apps.pt.md | 510 ++++--------------
1 file changed, 92 insertions(+), 418 deletions(-)
diff --git a/content/understand-django/2022-03-10-secure-apps.pt.md b/content/understand-django/2022-03-10-secure-apps.pt.md
index 97a73fb1..62f9dba8 100644
--- a/content/understand-django/2022-03-10-secure-apps.pt.md
+++ b/content/understand-django/2022-03-10-secure-apps.pt.md
@@ -1,14 +1,7 @@
---
title: "Security And Django"
description: >-
- You want to protect your users' privacy, right?
- The goal is noble
- and users demand it,
- but how do you do it?
- In this Understand Django article,
- we'll look at some areas
- that improve the security
- of your application.
+ You want to protect your users' privacy, right? The goal is noble and users demand it, but how do you do it? In this Understand Django article, we'll look at some areas that improve the security of your application.
image: img/django.png
type: post
categories:
@@ -24,15 +17,12 @@ series: "Understand Django"
In the last
{{< web >}}
-[Understand Django]({{< ref "/understand-django/_index.md" >}})
-article,
+[Understand Django]({{< ref "/understand-django/_index.pt.md" >}}) article,
{{< /web >}}
{{< book >}}
chapter,
{{< /book >}}
-we learned about where apps slow down.
-We explored techniques that help sites handle the load
-and provide a fast experience for users.
+we learned about where apps slow down. We explored techniques that help sites handle the load and provide a fast experience for users.
{{< web >}}
With this article,
@@ -40,18 +30,13 @@ With this article,
{{< book >}}
With this chapter,
{{< /book >}}
-we will look at security.
-How does a Django site stay safe
-on the big, bad internet?
-Let's find out.
+we will look at security. How does a Django site stay safe on the big, bad internet? Let's find out.
-{{< understand-django-series "security" >}}
+{{< understand-django-series-pt "security" >}}
## A Security Confession
-I have a confession to make.
-Of all the topics
-that I've covered about Django
+I have a confession to make. Of all the topics that I've covered about Django
{{< web >}}
in this series,
{{< /web >}}
@@ -60,73 +45,25 @@ in this book,
{{< /book >}}
*this is my least favorite one.*
{{< web >}}
-Perhaps that's why I've pushed the subject
-so far into this list of articles.
+Perhaps that's why I've pushed the subject so far into this list of articles.
{{< /web >}}
-I have a very hard time getting excited about security
-because it feels like a pure cost to me.
-As developers,
-we're in this arms race against malicious people
-who want to steal and profit
-from the data of others.
-In a perfect world,
-everyone would respect the privacy of others
-and leave private data alone.
-Alas,
-the world is far from perfect.
-
-The bad actors have devised clever and tricky methods
-of exploiting websites
-to steal data.
-Because of this,
-application developers have to implement guards
-in an attempt to prevent these exploits.
-Implementing those guards detract
-from the main objective
-of site building
-and often feels like a drag on efficiency.
-
-All that being said,
-**security is super important**.
-Even if you're like me
-and the topic doesn't naturally interest you
-(or actively feels like a waste of time),
-the security of your application matters.
+I have a very hard time getting excited about security because it feels like a pure cost to me. As developers, we're in this arms race against malicious people who want to steal and profit from the data of others. In a perfect world, everyone would respect the privacy of others and leave private data alone. Alas, the world is far from perfect.
+
+The bad actors have devised clever and tricky methods of exploiting websites to steal data. Because of this, application developers have to implement guards in an attempt to prevent these exploits. Implementing those guards detract from the main objective of site building and often feels like a drag on efficiency.
+
+All that being said, **security is super important**. Even if you're like me and the topic doesn't naturally interest you (or actively feels like a waste of time), the security of your application matters.
* Privacy matters.
* Trust matters.
-If we cannot protect the information
-that users of our Django sites bring,
-then trust will rapidly erode
-and, most likely,
-your users will disappear along with it.
-
-As noted in this section,
-security is not my favorite topic.
-I'm going to describe some security topics
-as they relate to Django,
-but if you want to learn from people who *love* security,
-then I would recommend reading
-from the
-{{< extlink "https://owasp.org/" "Open Web Application Security Project" >}}.
-This popular group can teach you far more
-about security
-than I can,
-and do it with gusto!
+If we cannot protect the information that users of our Django sites bring, then trust will rapidly erode and, most likely, your users will disappear along with it.
+
+As noted in this section, security is not my favorite topic. I'm going to describe some security topics as they relate to Django, but if you want to learn from people who *love* security, then I would recommend reading from the {{< extlink "https://owasp.org/" "Open Web Application Security Project" >}}. This popular group can teach you far more about security than I can, and do it with gusto!
## The Three **C**s
-Learning about security involves learning a bunch of acronyms.
-I don't know if this is something that security researchers
-like to do,
-or if the reason is because the problems
-that the acronyms stand for are challenging
-to understand.
-Either way,
-let's look at three common acronyms that start with C
-and the problems they address.
+Learning about security involves learning a bunch of acronyms. I don't know if this is something that security researchers like to do, or if the reason is because the problems that the acronyms stand for are challenging to understand. Either way, let's look at three common acronyms that start with C and the problems they address.
### CSRF
@@ -143,37 +80,18 @@ In the forms article,
{{< book >}}
In the forms chapter,
{{< /book >}}
-I did some hand waving and stated
-that you need a CSRF token
-for security reasons
-and basically said "trust me" at the time.
-
-CSRF stands for *Cross Site Request Forgery*.
-In simple terms,
-a CSRF attack allows an attacker to use someone's credentials
-to a different site
-without their permission.
-With a bit of imagination,
-you can see where this goes:
+I did some hand waving and stated that you need a CSRF token for security reasons and basically said "trust me" at the time.
+
+CSRF stands for *Cross Site Request Forgery*. In simple terms, a CSRF attack allows an attacker to use someone's credentials to a different site without their permission. With a bit of imagination, you can see where this goes:
* Attacker socially manipulates a user to click a link.
-* The click activity exploits the user's credentials to a site
- and changes something about the user's account
- like their email address.
+* The click activity exploits the user's credentials to a site and changes something about the user's account like their email address.
* The attacker changes the email address to something they control.
-* If the original site is something like an e-commerce site,
- the attacker may make purchases
- using the user's stored credit card information.
+* If the original site is something like an e-commerce site, the attacker may make purchases using the user's stored credit card information.
-Django includes a capability to help thwart this kind of attack.
-Through the use of **CSRF tokens**,
-we can help prevent bad actors
-from performing actions
-without user consent.
+Django includes a capability to help thwart this kind of attack. Through the use of **CSRF tokens**, we can help prevent bad actors from performing actions without user consent.
-A CSRF token works by including a generated value
-that gets submitted along with the form.
-The template looks like:
+A CSRF token works by including a generated value that gets submitted along with the form. The template looks like:
{{< web >}}
```django
@@ -194,8 +112,7 @@ The template looks like:
```
{{< /book >}}
-When this renders,
-the result would be something like:
+When this renders, the result would be something like:
```html
```
-The value would naturally be different
-from my example.
-When the form is submitted,
-the CSRF token gets checked for validity.
-A valid CSRF token is required
-to make a `POST` request,
-so this level of checking can help prevent attackers
-from changing a user's data
-on your site.
-
-You can learn more about CSRF
-with Django's
-{{< extlink "https://docs.djangoproject.com/en/4.1/ref/csrf/" "Cross Site Request Forgery protection" >}}
-reference page.
+The value would naturally be different from my example. When the form is submitted, the CSRF token gets checked for validity. A valid CSRF token is required to make a `POST` request, so this level of checking can help prevent attackers from changing a user's data on your site.
+
+You can learn more about CSRF with Django's {{< extlink "https://docs.djangoproject.com/en/4.1/ref/csrf/" "Cross Site Request Forgery protection" >}} reference page.
### CORS
-Imagine that you've built `myapp.com`.
-For your user interface,
-instead of Django templates,
-you built a client UI
-using a JavaScript framework
-like {{< extlink "https://vuejs.org/" "Vue.js" >}}.
-Your application is serving static files
-at `myapp.com`,
-and you built a Django-powered API
-that is handling the data
-which gets called at `api.myapp.com`.
-
-In this scenario,
-browsers will require you to set up CORS.
-CORS is *Cross-Origin Resource Sharing*.
-The goal of CORS is to help protect a domain
-from undesirable access.
-
-In our example,
-your API at `api.myapp.com` may only be designed
-to work with the user interface
-at `myapp.com`.
-With CORS,
-you can configure your API
-so that it will reject any requests
-that do not come
-from the `myapp.com` domain.
-This helps prevent bad actors
-from using `api.myapp.com`
-in the browser.
-
-Django does *not* include tools
-to handle CORS configuration
-from the core package.
-To make this work,
-you'll need to reach for a third party package.
-Since CORS configuration is handled
-through HTTP headers,
-you'll find that the very appropriately named
-{{< extlink "https://github.com/adamchainz/django-cors-headers" "django-cors-headers" >}} package
-is exactly what you need.
-
-I won't walk through the whole setup
-of that package
-because the README does a good job
-of explaining the process,
-but I will highlight the crucial setting.
-With django-cors-headers,
-you need to set the `CORS_ALLOWED_ORIGINS` list.
-Anything not in that list will be blocked
-by CORS controls in the browser.
-Our example configuration would look like:
+Imagine that you've built `myapp.com`. For your user interface, instead of Django templates, you built a client UI using a JavaScript framework like {{< extlink "https://vuejs.org/" "Vue.js" >}}. Your application is serving static files at `myapp.com`, and you built a Django-powered API that is handling the data which gets called at `api.myapp.com`.
+
+In this scenario, browsers will require you to set up CORS. CORS is *Cross-Origin Resource Sharing*. The goal of CORS is to help protect a domain from undesirable access.
+
+In our example, your API at `api.myapp.com` may only be designed to work with the user interface at `myapp.com`. With CORS, you can configure your API so that it will reject any requests that do not come from the `myapp.com` domain. This helps prevent bad actors from using `api.myapp.com` in the browser.
+
+Django does *not* include tools to handle CORS configuration from the core package. To make this work, you'll need to reach for a third party package. Since CORS configuration is handled through HTTP headers, you'll find that the very appropriately named {{< extlink "https://github.com/adamchainz/django-cors-headers" "django-cors-headers" >}} package is exactly what you need.
+
+I won't walk through the whole setup of that package because the README does a good job of explaining the process, but I will highlight the crucial setting. With django-cors-headers, you need to set the `CORS_ALLOWED_ORIGINS` list. Anything not in that list will be blocked by CORS controls in the browser. Our example configuration would look like:
```python
CORS_ALLOWED_ORIGINS = [
@@ -282,51 +145,22 @@ CORS_ALLOWED_ORIGINS = [
]
```
-As you read about CORS on the internet,
-you'll probably run into advice to set the HTTP header
-of `Access-Control-Allow-Origin: *`.
-This wildcard is what you'll get if you set `CORS_ALLOW_ALL_ORIGINS = True`
-in django-cors-headers.
-*This is probably not what you really want.
-Using this feature opts your site out of CORS protection.*
-Unless you have some public web API
-that is *designed* to work
-for many domains,
-you should try to avoid opting out of CORS.
-
-CORS is not a core concept that you will find in Django.
-If you want to learn more about the specifics of CORS,
-check out
-{{< extlink "https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS" "Cross-Origin Resource Sharing (CORS)" >}}
-from the Mozilla Developer Network (MDN).
+As you read about CORS on the internet, you'll probably run into advice to set the HTTP header of `Access-Control-Allow-Origin: *`. This wildcard is what you'll get if you set `CORS_ALLOW_ALL_ORIGINS = True` in django-cors-headers. *This is probably not what you really want. Using this feature opts your site out of CORS protection.* Unless you have some public web API that is *designed* to work for many domains, you should try to avoid opting out of CORS.
+
+CORS is not a core concept that you will find in Django. If you want to learn more about the specifics of CORS, check out {{< extlink "https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS" "Cross-Origin Resource Sharing (CORS)" >}} from the Mozilla Developer Network (MDN).
### CSP
-The final **C** in our tour
-is *Content Security Policy* or CSP, for short.
-You might roughly think of CSP
-as the inverse of CORS.
-Where CORS defines what parts of the internet can access your domain,
-CSP defines what your domain can access from the internet.
-
-The goal of CSP is to protect users on your site
-from running JavaScript
-(and other potentially harmful resources like images)
-from places that you don't want.
-
-To understand how your site can be vulnerable
-to these kinds of attacks,
-we need to understand a well-known attack vector
-called Cross-Site Scripting (XSS).
-XSS is when a bad actor finds a way to run a script
-on your domain.
-Here's a classic way that XSS can happen:
+The final **C** in our tour is *Content Security Policy* or CSP, for short. You might roughly think of CSP as the inverse of CORS. Where CORS defines what parts of the internet can access your domain, CSP defines what your domain can access from the internet.
+
+The goal of CSP is to protect users on your site from running JavaScript (and other potentially harmful resources like images) from places that you don't want.
+
+To understand how your site can be vulnerable to these kinds of attacks, we need to understand a well-known attack vector called Cross-Site Scripting (XSS). XSS is when a bad actor finds a way to run a script on your domain. Here's a classic way that XSS can happen:
* A site has a form that accepts text data.
* Then the site displays that text data *in its raw form*.
-At first,
-that seems harmless.
+At first, that seems harmless:
```html
@@ -334,10 +168,7 @@ that seems harmless.
```
-For an honest interaction like "What could possibly go wrong?"
-as user input,
-that is truly harmless.
-What about this?
+For an honest interaction like "What could possibly go wrong?" as user input, that is truly harmless. What about this?
```html
@@ -345,12 +176,7 @@ What about this?
```
-Now,
-the user added a bit of HTML markup.
-Again,
-this is fairly benign
-and will only add some unanticipated italics.
-What if the user is a bit more clever than that?
+Now, the user added a bit of HTML markup. Again, this is fairly benign and will only add some unanticipated italics. What if the user is a bit more clever than that?
```html
@@ -358,19 +184,9 @@ What if the user is a bit more clever than that?
```
-Here's where a site gets into trouble.
-If this is rendered on a page,
-a little alert box will appear.
-You can imagine this happening in a forum
-or some other sharing site
-where multiple people will see this output.
-That's annoying,
-but it's still not horrible.
+Here's where a site gets into trouble. If this is rendered on a page, a little alert box will appear. You can imagine this happening in a forum or some other sharing site where multiple people will see this output. That's annoying, but it's still not horrible.
-What does a really bad scenario look like?
-A really bad scenario is where the bad guys figure out
-that your site is unsafe in this way.
-Consider this:
+What does a really bad scenario look like? A really bad scenario is where the bad guys figure out that your site is unsafe in this way. Consider this:
```html
@@ -378,110 +194,28 @@ Consider this:
```
-Now your users are really in trouble.
-In this final version,
-the bad guys won.
-A user's browser will download and execute whatever JavaScript
-is in `owned.js`.
-This code could do all kinds of stuff
-like using the `fetch` API
-to run AJAX requests
-that can change the user's account credentials
-and steal their account.
-
-How do we defend against this kind of attack?
-There isn't a singular answer.
-In fact,
-multiple layers of protection is often what you really want.
-In security,
-this idea is called "defense-in-depth."
-If you have multiple layers to protect your site,
-then the site may become a less appealing target
-for attackers.
-
-For this particular scenario,
-we can use a couple of things
+Now your users are really in trouble. In this final version, the bad guys won. A user's browser will download and execute whatever JavaScript is in `owned.js`. This code could do all kinds of stuff like using the `fetch` API to run AJAX requests that can change the user's account credentials and steal their account.
+
+How do we defend against this kind of attack? There isn't a singular answer. In fact, multiple layers of protection is often what you really want. In security, this idea is called "defense-in-depth." If you have multiple layers to protect your site, then the site may become a less appealing target for attackers.
+
+For this particular scenario, we can use a couple of things
* HTML escaping of untrusted input
* CSP
-The real problem above is that the site is rendering user input
-without any modification.
-This is a problem with HTML
-because the raw characters are interpreted as HTML code
-and not just user data.
-
-The simplest solution is to make sure that any characters
-that mean something specific to HTML
-(like `<` or `>`)
-are replaced with escape codes (`<` or `>`)
-that will display the character
-in the browser
-without treating it like the actual HTML code character.
-**Django does this auto-escaping of user data by default**.
-You can disable this behavior for portions
-of a template
-using a variety of template tags
-like `autoescape` and the (ironically named?) `safe` tag.
-
-Because there are ways to opt out
-of safe behavior from HTML escaping
-and because clever attackers might find other ways
-to inject script calls
-into your site,
-CSP is another layer of protection.
-
-Primarily, CSP is possible
-with a `Content-Security-Policy` HTTP header.
-You can read all of the gritty details
-on the
-{{< extlink "https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP" "Content Security Policy (CSP)" >}}
-article on MDN.
-Like CORS,
-CSP is not something that Django supports out-of-the-box.
-Thankfully, Mozilla (yep, the same Mozilla from MDN),
-offers a
-{{< extlink "https://django-csp.readthedocs.io/en/latest/index.html" "django-csp" >}} package
-that you can use to configure an appropriate policy
-for your Django site.
-
-In a content security policy,
-you mark everything that you want to allow.
-This fundamentally changes what requests your site will connect to.
-Instead of allowing everything by default,
-the site operates on a model that denies things by default.
-With a "deny by default" stance,
-you can then pick resources
-which you deem are safe for your site.
-Modern browsers respect the policy declared
-by the HTTP header
-and will refuse to connect to resources
-outside of your policy
-when users visit your site at your domain.
-
-There is something obvious
-that we should get out of the way.
-This setup and configuration requires *more work*.
-Having more secure systems requires effort,
-study,
-and plenty of frustration
-as you make a site more secure.
-The benefits to your users or customers is
-that their data stays safe.
-I think most people expect this level of protection
-by default.
-
-So,
-do you have to become a security expert
-to build websites?
-I don't think so.
-There is a set of fundamental issues
-with web application security
-that you should know about
-(and we've covered some of those issues already),
-but you don't need to be prepared
-to go to black hat conventions
-in order to work on the web.
+The real problem above is that the site is rendering user input without any modification. This is a problem with HTML because the raw characters are interpreted as HTML code and not just user data.
+
+The simplest solution is to make sure that any characters that mean something specific to HTML (like `<` or `>`) are replaced with escape codes (`<` or `>`) that will display the character in the browser without treating it like the actual HTML code character. **Django does this auto-escaping of user data by default**. You can disable this behavior for portions of a template using a variety of template tags like `autoescape` and the (ironically named?) `safe` tag.
+
+Because there are ways to opt out of safe behavior from HTML escaping and because clever attackers might find other ways to inject script calls into your site, CSP is another layer of protection.
+
+Primarily, CSP is possible with a `Content-Security-Policy` HTTP header. You can read all of the gritty details on the {{< extlink "https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP" "Content Security Policy (CSP)" >}} article on MDN. Like CORS, CSP is not something that Django supports out-of-the-box. Thankfully, Mozilla (yep, the same Mozilla from MDN), offers a {{< extlink "https://django-csp.readthedocs.io/en/latest/index.html" "django-csp" >}} package that you can use to configure an appropriate policy for your Django site.
+
+In a content security policy, you mark everything that you want to allow. This fundamentally changes what requests your site will connect to. Instead of allowing everything by default, the site operates on a model that denies things by default. With a "deny by default" stance, you can then pick resources which you deem are safe for your site. Modern browsers respect the policy declared by the HTTP header and will refuse to connect to resources outside of your policy when users visit your site at your domain.
+
+There is something obvious that we should get out of the way. This setup and configuration requires *more work*. Having more secure systems requires effort, study, and plenty of frustration as you make a site more secure. The benefits to your users or customers is that their data stays safe. I think most people expect this level of protection by default.
+
+So, do you have to become a security expert to build websites? I don't think so. There is a set of fundamental issues with web application security that you should know about (and we've covered some of those issues already), but you don't need to be prepared to go to black hat conventions in order to work on the web.
{{< web >}}
Before finishing up this security article,
@@ -489,90 +223,40 @@ Before finishing up this security article,
{{< book >}}
Before finishing up this security chapter,
{{< /book >}}
-let's look at what Django provides
-so that you can be less of a security expert
-and use the knowledge
-of the community.
+let's look at what Django provides so that you can be less of a security expert and use the knowledge of the community.
## Check Command Revisited
-The Django documentation includes a good overview
-of the framework's security features
-on the
-{{< extlink "https://docs.djangoproject.com/en/dev/topics/security/" "Security in Django" >}} page.
+The Django documentation includes a good overview of the framework's security features on the {{< extlink "https://docs.djangoproject.com/en/dev/topics/security/" "Security in Django" >}} page.
-Aside from the content outlined
-on the security page,
-we can return to the check command
+Aside from the content outlined on the security page, we can return to the check command
{{< web >}}
discussed in previous articles.
{{< /web >}}
{{< book >}}
discussed in previous chapters.
{{< /book >}}
-Recall that Django includes a `check` command
-that can check your site's configuration
-before you deploy a site live.
-The command looks like:
+Recall that Django includes a `check` command that can check your site's configuration before you deploy a site live. The command looks like:
```bash
$ ./manage.py check --deploy
```
-The output from this command can show
-where your configuration is less than ideal
-from a security perspective.
-
-The security warnings that come
-from running the `check` command are defined
-in `django.core.checks.security`.
-A more readable version
-of the available security checks is
-on the
-{{< extlink "https://docs.djangoproject.com/en/4.1/ref/checks/#security" "System check framework" >}}
-reference page.
-
-Scanning through the list
-of checks,
-you'll find that
-
-* many checks center around configuring your site
- to run with HTTPS.
- Secure connections used to reference SSL
- for Secure Sockets Layer.
- Along the way,
- that layer changed names to TLS
- for Transport Layer Security.
- In practice,
- if you see either of those terms,
- think `https://`.
-* other checks confirm that your site has the kinds
- of
- {{< extlink "https://docs.djangoproject.com/en/4.1/ref/middleware/#security-middleware" "middleware" >}}
- installed
- that offer some
- of the protection discussed previously
- (like CSRF).
-* still other checks look for core settings
- that should be set
- like `DEBUG = False`
- and defining the `ALLOWED_HOSTS` setting.
-
-There is a good comment in the Django security checks reference docs
-that is worth repeating here:
-
-> The security checks do not make your site secure.
-They do not audit code, do intrusion detection, or do anything particularly complex.
-Rather, they help perform an automated, low-hanging-fruit checklist,
-that can help you to improve your site’s security.
-
-When you're thinking through security,
-do some homework
-and don't let your brain go on autopilot.
-Remember that users develop a trust relationship
-with your websites.
-That trust is easy to break
-and may cause people to leave your site forever.
+The output from this command can show where your configuration is less than ideal from a security perspective.
+
+The security warnings that come from running the `check` command are defined in `django.core.checks.security`. A more readable version of the available security checks is on the {{< extlink "https://docs.djangoproject.com/en/4.1/ref/checks/#security" "System check framework" >}} reference page.
+
+Scanning through the list of checks, you'll find that:
+
+* many checks center around configuring your site to run with HTTPS. Secure connections used to reference SSL for Secure Sockets Layer. Along the way, that layer changed names to TLS for Transport Layer Security. In practice, if you see either of those terms, think `https://`.
+* other checks confirm that your site has the kinds of {{< extlink "https://docs.djangoproject.com/en/4.1/ref/middleware/#security-middleware" "middleware" >}} installed that offer some of the protection discussed previously (like CSRF).
+* still other checks look for core settings that should be set like `DEBUG = False` and defining the `ALLOWED_HOSTS` setting.
+
+There is a good comment in the Django security checks reference docs that is worth repeating here:
+
+> The security checks do not make your site secure. They do not audit code, do intrusion detection, or do anything particularly complex. Rather, they help perform an automated, low-hanging-fruit checklist, that can help you to improve your site’s security.
+
+When you're thinking through security, do some homework and don't let your brain go on autopilot. Remember that users develop a trust relationship with your websites. That trust is easy to break and may cause people to leave your site forever.
## Summary
@@ -582,25 +266,24 @@ In this article,
{{< book >}}
In this chapter,
{{< /book >}}
-we explored security topics
-and how they relate to Django.
+we explored security topics and how they relate to Django.
+
We covered:
* CSRF
* CORS
* CSP
* Cross-site scripting (XSS)
-* The security checks and information available
- from the `check` command
+* The security checks and information available from the `check` command
{{< web >}}
-In the *last* article
-of the Understand Django series,
+In the *last* article of the Understand Django series,
{{< /web >}}
{{< book >}}
In the *last* chapter,
{{< /book >}}
we'll get into debugging.
+
You'll learn about:
* Debugging tools like `pdb`
@@ -608,15 +291,6 @@ You'll learn about:
* Strategies for finding and fixing problems
{{< web >}}
-If you'd like to follow along
-with the series,
-please feel free to sign up
-for my newsletter
-where I announce all of my new content.
-If you have other questions,
-you can reach me online
-on Twitter
-where I am
-{{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
+If you'd like to follow along with the series, please feel free to sign up for my newsletter where I announce all of my new content. If you have other questions, you can reach me online on Twitter where I am {{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
{{< /web >}}
From 7d2ca7cc80b578f5b24d01cc68f950a0762e9576 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nazar=C3=A9=20da=20Piedade?=
Date: Fri, 5 May 2023 11:21:25 +0100
Subject: [PATCH 21/30] chore: format
`understand-django/2022-05-31-debugging-tips-techniques.pt.md`
---
...2022-05-31-debugging-tips-techniques.pt.md | 738 ++++--------------
1 file changed, 139 insertions(+), 599 deletions(-)
diff --git a/content/understand-django/2022-05-31-debugging-tips-techniques.pt.md b/content/understand-django/2022-05-31-debugging-tips-techniques.pt.md
index 39da3d31..d981f5cd 100644
--- a/content/understand-django/2022-05-31-debugging-tips-techniques.pt.md
+++ b/content/understand-django/2022-05-31-debugging-tips-techniques.pt.md
@@ -1,12 +1,7 @@
---
title: "Debugging Tips And Techniques"
description: >-
- Your Django app is up.
- You've got users.
- Your users are hitting bugs.
- How do you debug
- to fix the problems?
- That's the focus of this Understand Django article.
+ Your Django app is up. You've got users. Your users are hitting bugs. How do you debug to fix the problems? That's the focus of this Understand Django article.
image: img/django.png
type: post
categories:
@@ -22,15 +17,12 @@ series: "Understand Django"
In the last
{{< web >}}
-[Understand Django]({{< ref "/understand-django/_index.md" >}})
-article,
+[Understand Django]({{< ref "/understand-django/_index.pt.md" >}}) article,
{{< /web >}}
{{< book >}}
chapter,
{{< /book >}}
-we looked at security.
-How does a Django site stay safe
-on the big, bad internet?
+we looked at security. How does a Django site stay safe on the big, bad internet?
{{< web >}}
The article explored some core elements
{{< /web >}}
@@ -45,236 +37,88 @@ With this article,
{{< book >}}
With this chapter,
{{< /book >}}
-we will investigate problem solving techniques
-for Django apps.
-The goal is to equip you with tools
-to fix the real problems
-that you'll hit when building your Django site.
+we will investigate problem solving techniques for Django apps. The goal is to equip you with tools to fix the real problems that you'll hit when building your Django site.
-{{< understand-django-series "debugging" >}}
+{{< understand-django-series-pt "debugging" >}}
## Systematic Problem Solving
-When you have real users using your website
-and they report problems with the site,
-what can you do to fix the problems?
-Having a mental framework
-for how to fix problems is really useful,
-because it provides a repeatable process
-that you'll be able to use as long
-as you're building on the web.
-Let's discuss the mental framework
-that I use,
-which I think of as *systematic problem solving*.
-
-The first thing to drill into your head is
-that computers are deterministic.
-Given a set of inputs,
-they will produce a repeatable output.
-This is crucial to remember
-because the art of problem solving
-with computers
-is about sleuthing the state
-of your system
-(i.e., a Django website in this context)
-to determine why a given set of inputs
-produced an undesirable output,
-also known as a *bug*.
-
-When debugging a problem,
-the challenge is figuring out what all those inputs are.
-The inputs can vary wildly.
-Successful problem solvers are those
-who can find the inputs
-that caused the bad output.
-
-This is the core of systematic problem solving.
-With systematic problem solving,
-you want repeatable patterns
-that help you build up a mental model
-of how a problem occurred.
-Once you understand the problem,
-you'll be ready to create the solution
-and fix your bug.
+When you have real users using your website and they report problems with the site, what can you do to fix the problems? Having a mental framework for how to fix problems is really useful, because it provides a repeatable process that you'll be able to use as long as you're building on the web. Let's discuss the mental framework that I use, which I think of as *systematic problem solving*.
+
+The first thing to drill into your head is that computers are deterministic. Given a set of inputs, they will produce a repeatable output. This is crucial to remember because the art of problem solving with computers is about sleuthing the state of your system (i.e., a Django website in this context) to determine why a given set of inputs produced an undesirable output, also known as a *bug*.
+
+When debugging a problem, the challenge is figuring out what all those inputs are. The inputs can vary wildly. Successful problem solvers are those who can find the inputs that caused the bad output.
+
+This is the core of systematic problem solving. With systematic problem solving, you want repeatable patterns that help you build up a mental model of how a problem occurred. Once you understand the problem, you'll be ready to create the solution and fix your bug.
*How do you repeatably figure out problems with a website?*
-In my experience,
-the most fruitful way to understand a problem is to reproduce it.
-To reproduce a problem requires context and data
-about what happened in the first place.
+In my experience, the most fruitful way to understand a problem is to reproduce it. To reproduce a problem requires context and data about what happened in the first place.
What are the sources of this data?
{{< web >}}
-* Error monitoring services like Rollbar or Sentry can be a fantastic source
- of data.
+* Error monitoring services like Rollbar or Sentry can be a fantastic source of data.
* Log data, which we'll discuss later in this article, can be another excellent source.
{{< /web >}}
{{< book >}}
-* Error monitoring services like Rollbar or Sentry can be a fantastic source
- of data.
+* Error monitoring services like Rollbar or Sentry can be a fantastic source of data.
* Log data, which we'll discuss later in this chapter, can be another excellent source.
{{< /book >}}
-Error monitoring services help show what happened.
-These services will collect tracebacks of Python exceptions
-and display them in context
-of a particular request.
-Some of these tools can even show the sequence of events
-that led to the failure case.
-
-My favorite strategy for cases like this is to produce
-an automated test
-that can trigger the same exception
-as the one reported by the error monitoring service.
-This is often a mechanical transformation
-of the data provided on an error report page
-into the setup data
-of a unit test.
-
-The great part about having a unit test is that you now have something
-that you can repeat with ease.
-This is a lot better than clicking around on your site
-and trying to recreate a problem manually.
-Because you've captured the problem scenario in test code,
-you can know *exactly* when you've **fixed** the problem.
-You can work on the site's code and run the test over and over
-until you've devised a solution
-that overcomes your error.
-
-This process is a systematic approach
-that you can apply again and again
-to solve problems.
-The process in a nutshell looks like:
+Error monitoring services help show what happened. These services will collect tracebacks of Python exceptions and display them in context of a particular request. Some of these tools can even show the sequence of events that led to the failure case.
+
+My favorite strategy for cases like this is to produce an automated test that can trigger the same exception as the one reported by the error monitoring service. This is often a mechanical transformation of the data provided on an error report page into the setup data of a unit test.
+
+The great part about having a unit test is that you now have something that you can repeat with ease. This is a lot better than clicking around on your site and trying to recreate a problem manually. Because you've captured the problem scenario in test code, you can know *exactly* when you've **fixed** the problem. You can work on the site's code and run the test over and over until you've devised a solution that overcomes your error.
+
+This process is a systematic approach that you can apply again and again to solve problems. The process in a nutshell looks like:
* Collect data from the failure scenario
-* Transform that data into an automated test
- that demonstrates the problem in a repeatable fashion.
-* Fix your site's code until the test passes
- and the problem is resolved.
-
-The secondary benefit of this systematic model is
-that your site has a passing unit test when you're done.
-This test acts as a guard against future failures
-of a similar kind (i.e., a "regression" test).
-
-I don't want to oversell the value
-of these kinds of tests.
-Regression tests can be useful,
-but in my experience,
-I've rarely seen regression tests fail
-after the offending code is fixed.
-Nonetheless,
-if the test is fast enough,
-these kinds of tests are worth keeping
-in your automated test suite
-for those rare cases where you *do* encounter a regression failure.
-
-Maybe my unit testing approach doesn't appeal to you.
-That's fine.
-Do what works well for you.
-My larger point is that you should try to take a systematic approach
-to problem solving.
-**Find repeatable patterns that you can apply to your context.**
-
-Approaching problems with a process in hand helps you avoid feeling stuck.
-Having methods for solving problems also helps
-when the pressure is on,
-and you're trying to fix a critical and time sensitive bug
-for your customers.
-Not knowing what to do in a high pressure scenario can add even more stress
-to an already stressful situation.
+* Transform that data into an automated test that demonstrates the problem in a repeatable fashion.
+* Fix your site's code until the test passes and the problem is resolved.
+
+The secondary benefit of this systematic model is that your site has a passing unit test when you're done. This test acts as a guard against future failures of a similar kind (i.e., a "regression" test).
+
+I don't want to oversell the value of these kinds of tests. Regression tests can be useful, but in my experience, I've rarely seen regression tests fail after the offending code is fixed. Nonetheless, if the test is fast enough, these kinds of tests are worth keeping in your automated test suite for those rare cases where you *do* encounter a regression failure.
+
+Maybe my unit testing approach doesn't appeal to you. That's fine. Do what works well for you. My larger point is that you should try to take a systematic approach to problem solving. **Find repeatable patterns that you can apply to your context.**
+
+Approaching problems with a process in hand helps you avoid feeling stuck. Having methods for solving problems also helps when the pressure is on, and you're trying to fix a critical and time sensitive bug for your customers. Not knowing what to do in a high pressure scenario can add even more stress to an already stressful situation.
## `print` Without Shame
-In the previous section,
-I wrote "Fix your site's code until the test passes,"
-but I didn't explain how you'd actually do that.
-
-When you're working with a failing test
-and you're trying to make it pass
-to solve a problem,
-you have to understand the scenario
-at a pretty deep level.
-
-One tool to reach for is the `print` function.
-You can sprinkle some calls to `print`
-into the code you're working with
-to gain an understanding
-of what is happening.
-
-In your journey in programming,
-you'll run across this idea
-that "print debugging" is a bad idea.
-Not everyone espouses this idea,
-but it exists out there.
-In the same breath,
-you'll read that you should be using a debugger instead
-or some other tool.
-
-Debuggers,
-which are great tools
-that we'll cover shortly,
-are not the only tool worth knowing.
-In fact,
-in many cases,
-debuggers are a terrible tool for the job.
-
-What does the `print` function do
-that makes it so useful?
-**`print` provides a chronology.**
-
-With the `print` function,
-you can observe changes *over time*.
-The `print` function can answer questions like:
-
-* What is the value of my variable
- in each iteration through the loop?
-* Is this block of code even reached
- by the interpreter?
-* What is the before and after state
- after a section of code executes?
-
-The nice part about using `print`
-in an automated test is that you can see all the data,
-all at once.
-The results of a handful of `print` calls starts
-to tell a story
-of what happened during execution.
-If your "story" isn't rich enough and meaningful enough
-to understand what's happening,
-you add more `print` calls
-and run the test again.
-
-I'd estimate that I am able to use `print`
-for 70-80% of my debugging needs.
+In the previous section, I wrote "Fix your site's code until the test passes," but I didn't explain how you'd actually do that.
+
+When you're working with a failing test and you're trying to make it pass to solve a problem, you have to understand the scenario at a pretty deep level.
+
+One tool to reach for is the `print` function. You can sprinkle some calls to `print` into the code you're working with to gain an understanding of what is happening.
+
+In your journey in programming, you'll run across this idea that "print debugging" is a bad idea. Not everyone espouses this idea, but it exists out there. In the same breath, you'll read that you should be using a debugger instead or some other tool.
+
+Debuggers, which are great tools that we'll cover shortly, are not the only tool worth knowing. In fact, in many cases, debuggers are a terrible tool for the job.
+
+What does the `print` function do that makes it so useful? **`print` provides a chronology.**
+
+With the `print` function, you can observe changes *over time*. The `print` function can answer questions like:
+
+* What is the value of my variable in each iteration through the loop?
+* Is this block of code even reached by the interpreter?
+* What is the before and after state after a section of code executes?
+
+The nice part about using `print` in an automated test is that you can see all the data, all at once. The results of a handful of `print` calls starts to tell a story of what happened during execution. If your "story" isn't rich enough and meaningful enough to understand what's happening, you add more `print` calls and run the test again.
+
+I'd estimate that I am able to use `print` for 70-80% of my debugging needs.
What should you print?
-* One simple strategy is to print a number sequence.
- Adding `print(1)`, `print(2)`, or more
- to various lines
- can show what parts of code are executing
- or what patterns are happening in loops.
-* Sometimes I'll print the values of conditionals
- for branches that I'm interested in.
- This helps me figure out when certain branches
- in the code are taken versus when they are not.
-* Other times I'll add prints
- with a bunch of newlines
- to visually separate things.
- `print('\n\nHERE\n\n')` is surprisingly effective.
-
-The answer to "what should you print?" is really
-"what do you want to know?"
-As you use `print`,
-you'll develop a good feel
-for what information you find most useful.
-
-Tip: As of Python 3.8,
-you can print the variable name
-in an f-string along with its value:
+* One simple strategy is to print a number sequence. Adding `print(1)`, `print(2)`, or more to various lines can show what parts of code are executing or what patterns are happening in loops.
+* Sometimes I'll print the values of conditionals for branches that I'm interested in. This helps me figure out when certain branches in the code are taken versus when they are not.
+* Other times I'll add prints with a bunch of newlines to visually separate things. `print('\n\nHERE\n\n')` is surprisingly effective.
+
+The answer to "what should you print?" is really "what do you want to know?" As you use `print`, you'll develop a good feel for what information you find most useful.
+
+Tip: As of Python 3.8, you can print the variable name in an f-string along with its value:
```python
>>> foo = 5
@@ -282,340 +126,86 @@ in an f-string along with its value:
Hello world foo=5
```
-`print` is pretty great,
-but it isn't always my tool
-of choice.
-For instance, `print` is not the best tool
-when problem solving a live site.
-`print` also doesn't work as well
-when I need to get really detailed information
-about the state of my program.
+`print` is pretty great, but it isn't always my tool of choice. For instance, `print` is not the best tool when problem solving a live site. `print` also doesn't work as well when I need to get really detailed information about the state of my program.
-When do I reach for something else?
-Let's look at debuggers next.
+When do I reach for something else? Let's look at debuggers next.
## Debuggers
-If 70-80% of the time I can get away with `print`,
-what about the rest of the time?
-
-Sometimes I'll use a debugger.
-A debugger is a specialized tool
-that allows you to go through your Python code,
-one line at a time.
-
-Considering how much code we write
-and how much we use from other packages,
-this means that a debugger can be a slow tool
-to utilize.
-A debugger makes up for this slowness
-with an unparalleled amount
-of information
-about what is going on
-at the exact moment
-that the Python interpreter executes a line of code.
+If 70-80% of the time I can get away with `print`, what about the rest of the time?
+
+Sometimes I'll use a debugger. A debugger is a specialized tool that allows you to go through your Python code, one line at a time.
+
+Considering how much code we write and how much we use from other packages, this means that a debugger can be a slow tool to utilize. A debugger makes up for this slowness with an unparalleled amount of information about what is going on at the exact moment that the Python interpreter executes a line of code.
How do you use a debugger?
-Assuming that you're following my strategy
-of writing an automated test
-when you're working on a problem,
-the simplest way to start a debugger is
-by adding `breakpoint()`
-before the code you want to check.
-
-In a standard Python installation,
-adding this function call will pause your program
-by running the Python debugger, `pdb`,
-starting from the call to `breakpoint`.
-
-If you do this, you'll be left at a prompt
-that starts with `(Pdb)`.
-From here,
-you'll need to know some commands
-to navigate within the debugger.
-I'll cover the primary commands I use here,
-but you can type `h`
-to see a list of the available commands
-with instructions for how to get more help info.
-
-My natural inclination in the debugger is to know where I am.
-The `l` command will *list* code
-that the interpreter is about to execute,
-along with an arrow showing the next line
-that Python will run.
-I may also want to know where I am in the call stack
-(i.e., the history of calls that go back all the way
-to the main function
-that started the Python process).
-By using the `w` command to show *where* I am,
-pdb will show the call stack
-with the current line of code listed last
-by the prompt
-and the oldest function listed
-at the top of the output.
-These two commands give me the context
-at any particular moment.
-
-Next,
-I'll often want to know the values
-of local variables
-when I'm debugging.
-I can either use `p` to *print* the value
-or `pp` to *pretty print*
-in cases when I have a structure like a list or dictionary.
-
-All the previous commands orient me
-to where my code is and what values are in the data structures.
-With that context,
-I'm ready to work through my code
-with two additional commands.
-In a debugger,
-you advance the Python interpreter a line at a time.
-There are two styles to do this.
-
-One way is with the `n` command
-to go to the *next* line.
-This command is what you want
-when you don't really care about how a line runs.
-For instance,
-if the line calls a function that you know works,
-`n` is the way to pass that line
-to let it execute in its entirety.
-
-The other command to advance the interpreter is the `s` command.
-The `s` command lets you *step* through the code
-at the smallest possible increment.
-That means if you're on a line
-with a function call,
-the `s` command will move *into* the function call
-to show what's executing inside of it.
-I view the step command as the very fine adjustment command
-to run as I get close to the problem area
-in my code.
-
-Whenever I'm done debugging my code
-and have seen all that I need to see,
-I finish with the `c` command
-to *continue* normal execution.
-
-What's great about having a unit test is that I can run the debugger
-in a very coarse way
-by liberally using the `n` command.
-As I hit the error state
-(like an exception happening),
-I can continue,
-restart the test,
-then quickly return to right before the point in time
-when the error occurs.
-At that juncture,
-I switch to the `s` command
-until I find the exact moment
-when something goes wrong.
-This process helps me avoid wasting time
-by stepping through parts of the code that don't matter.
-
-The debugger has more interesting features
-like setting breakpoints
-and checking data at different parts
-of the call stack,
-but I hope this description gives you an idea
-of how to apply a debugger
-in your workflow.
-
-Learn more about the debugger and `pdb`
-by checking out the
-{{< extlink "https://docs.python.org/3/library/pdb.html" "pdb standard libary documentation" >}}.
+Assuming that you're following my strategy of writing an automated test when you're working on a problem, the simplest way to start a debugger is by adding `breakpoint()` before the code you want to check.
+
+In a standard Python installation, adding this function call will pause your program by running the Python debugger, `pdb`, starting from the call to `breakpoint`.
+
+If you do this, you'll be left at a prompt that starts with `(Pdb)`. From here, you'll need to know some commands to navigate within the debugger. I'll cover the primary commands I use here, but you can type `h` to see a list of the available commands with instructions for how to get more help info.
+
+My natural inclination in the debugger is to know where I am. The `l` command will *list* code that the interpreter is about to execute, along with an arrow showing the next line that Python will run. I may also want to know where I am in the call stack (i.e., the history of calls that go back all the way to the main function that started the Python process). By using the `w` command to show *where* I am, pdb will show the call stack with the current line of code listed last by the prompt and the oldest function listed at the top of the output. These two commands give me the context at any particular moment.
+
+Next, I'll often want to know the values of local variables when I'm debugging. I can either use `p` to *print* the value or `pp` to *pretty print* in cases when I have a structure like a list or dictionary.
+
+All the previous commands orient me to where my code is and what values are in the data structures. With that context, I'm ready to work through my code with two additional commands. In a debugger, you advance the Python interpreter a line at a time. There are two styles to do this.
+
+One way is with the `n` command to go to the *next* line. This command is what you want when you don't really care about how a line runs. For instance, if the line calls a function that you know works, `n` is the way to pass that line to let it execute in its entirety.
+
+The other command to advance the interpreter is the `s` command. The `s` command lets you *step* through the code at the smallest possible increment. That means if you're on a line with a function call, the `s` command will move *into* the function call to show what's executing inside of it. I view the step command as the very fine adjustment command to run as I get close to the problem area in my code.
+
+Whenever I'm done debugging my code and have seen all that I need to see, I finish with the `c` command to *continue* normal execution.
+
+What's great about having a unit test is that I can run the debugger in a very coarse way by liberally using the `n` command. As I hit the error state (like an exception happening), I can continue, restart the test, then quickly return to right before the point in time when the error occurs. At that juncture, I switch to the `s` command until I find the exact moment when something goes wrong. This process helps me avoid wasting time by stepping through parts of the code that don't matter.
+
+The debugger has more interesting features like setting breakpoints and checking data at different parts of the call stack, but I hope this description gives you an idea of how to apply a debugger in your workflow.
+
+Learn more about the debugger and `pdb` by checking out the {{< extlink "https://docs.python.org/3/library/pdb.html" "pdb standard libary documentation" >}}.
## Use The Source, Luke
-At times,
-you're going to run into problems
-with code
-that is not part of your project.
-This might be code
-in the Django source
-or it might be code
-from some other library
-that you happen to use in your project.
-
-What I have found over time is
-that many less experienced developers
-will hit this situation
-and suddenly freeze.
-There's some kind of mental barrier preventing them
-from looking any further
-than the boundary
-of their own code.
+At times, you're going to run into problems with code that is not part of your project. This might be code in the Django source or it might be code from some other library that you happen to use in your project.
+
+What I have found over time is that many less experienced developers will hit this situation and suddenly freeze. There's some kind of mental barrier preventing them from looking any further than the boundary of their own code.
Here's my tip: **don't freeze!**
-When you wake up
-to the reality
-that it's all just code
-that people wrote,
-you cross that mental barrier
-and move into realms
-that others may fear to tread.
-The funny part is that this code is often no more special
-than anything that you might write yourself!
-In fact,
-many open source developers are fantastic programmers,
-and fantastic programmers often know how to write clear,
-maintainable,
-and well-documented code.
-
-So,
-listen to your inner "Obi-Wan"
-and use the *source*, Luke!
-
-* If you're hitting a problem with a package,
- look at the source code on GitHub
- (or wherever the code is hosted).
- You can study the modules and trace through how the code would execute.
-* If that's not enough,
- don't be afraid to step through functions
- in other libraries
- with your debugger
- as you are testing out whatever problem
- that you are trying to fix.
-* Remember `print` debugging?
- Who says you can't do that with other software?
- If I've got a virtual environment
- and I need to figure out what's going on
- with how my software interacts with a library,
- I will sometimes add `print` statements directly
- to the library files
- in my virtual environment's `site-packages` directory.
- This is a great trick!
-
-There are limitations to this approach because some packages
-will include code sections that are compiled with C.
-That kind of code is harder to peek into,
-but I've found that the majority of Python packages
-that you find on PyPI are Python-only.
-That makes most packages great candidates
-for digging into the source code.
+When you wake up to the reality that it's all just code that people wrote, you cross that mental barrier and move into realms that others may fear to tread. The funny part is that this code is often no more special than anything that you might write yourself! In fact, many open source developers are fantastic programmers, and fantastic programmers often know how to write clear, maintainable, and well-documented code.
+
+So, listen to your inner "Obi-Wan" and use the *source*, Luke!
+
+* If you're hitting a problem with a package, look at the source code on GitHub (or wherever the code is hosted). You can study the modules and trace through how the code would execute.
+* If that's not enough, don't be afraid to step through functions in other libraries with your debugger as you are testing out whatever problem that you are trying to fix.
+* Remember `print` debugging? Who says you can't do that with other software? If I've got a virtual environment and I need to figure out what's going on with how my software interacts with a library, I will sometimes add `print` statements directly to the library files in my virtual environment's `site-packages` directory. This is a great trick!
+
+There are limitations to this approach because some packages will include code sections that are compiled with C. That kind of code is harder to peek into, but I've found that the majority of Python packages that you find on PyPI are Python-only. That makes most packages great candidates for digging into the source code.
## Logging Chronology
-Logging is a topic that we will only scratch the surface of,
-but the subject is important
-to round out a discussion of debugging tools.
-
-A logging system provides the ability to record messages
-during the execution
-of your application.
-The easiest way to think of logging is as a permanent set
-of `print` calls
-that record information every time your functions or methods
-call the logging system.
-
-When logging is used in an application,
-you can capture what happened within your app
-even if you weren't there to observe the system
-at the moment that it happened.
-The logging system forms a chronological log
-of events
-that happen within a system.
-
-This has some big benefits,
-but comes with some tradeoffs
-to consider.
-
-* **Pro**: When you have sufficient logging,
- you can see what is happening in the app
- when your users are working in the app.
-* **Pro**: More advanced logging configurations
- can include metadata that you can filter on
- to look for specific kinds of activity.
- This can aid your diagnostic abilities.
-* **Con**: Logging generates data.
- While this enables the points above,
- you now have a new challenge
- of *managing* this new source of data.
-* **Con**: Log management is even more of a challenge
- as an application system grows.
- When you have multiple servers,
- where does all that log data go?
-
-This concept of logging is not a Django-specific idea.
-In fact,
-you can find a `logging` module
-in the Python standard library
-that serves as the basis
-for logging in the Python world.
-Django builds its logging features
-on top of the `logging` module.
-
-The short version of logging
-in Django
-is that you can use logging
-by configuring the `LOGGING` settings
-in your Django settings module.
-This process is described
-in the documentation
-at {{< extlink "https://docs.djangoproject.com/en/4.1/howto/logging/" "How to configure and use logging" >}}.
-
-In a basic workflow,
-logging can be little more
-than writing lines
-of data
-to a log *file*.
-If you're running an app
-on a VPS
-like Digital Ocean
-and have control over your filesystem,
-this is a good starting option.
-If you choose that path,
-keep in mind that disk space is a limited resource.
-One way to tank your app is to fill your machine's disk
-with logging data.
-To prevent this,
-you should investigate how to use logrotate.
-{{< extlink "https://linux.die.net/man/8/logrotate" "logrotate" >}} is a command
-that can archive your log data
-on whatever frequency you desire
-and clean up old data
-to prevent your storage from filling completely.
-
-You may follow a path like I've recommended frequently
-and deploy your app on a Platform as a Service (PaaS) like Heroku.
-In that kind of environment,
-you have less control over the tools that are available
-to run your application.
-In the case of Heroku,
-the platform does not guarantee
-that your app will stay on the same machine.
-Because of that constraint,
-you can't rely on a log file
-on the machine.
-Instead,
-you would need to add an additional service
-to the application
-that will store logs
-for your later use.
-{{< extlink "https://www.papertrail.com/" "Papertrail" >}} is an example
-of a log aggregation service
-that works with Heroku.
-
-As you can see,
-logging brings some complexity to manage
-if you choose to use it.
-Logs can be a great tool
-to understand what happened
-when a user used your app,
-especially if there was an error
-that didn't raise an exception.
-For me, personally,
-I *don't* reach for logging right away.
-While logging can be useful,
-it can be an overwhelming amount of data
-that can turn debugging
-into a "needle in a haystack" problem.
-Logging is still a potentially useful tool
-for your toolbox
-as you think about how to debug your apps.
+Logging is a topic that we will only scratch the surface of, but the subject is important to round out a discussion of debugging tools.
+
+A logging system provides the ability to record messages during the execution of your application. The easiest way to think of logging is as a permanent set of `print` calls that record information every time your functions or methods call the logging system.
+
+When logging is used in an application, you can capture what happened within your app even if you weren't there to observe the system at the moment that it happened. The logging system forms a chronological log of events that happen within a system.
+
+This has some big benefits, but comes with some tradeoffs to consider.
+
+* **Pro**: When you have sufficient logging, you can see what is happening in the app when your users are working in the app.
+* **Pro**: More advanced logging configurations can include metadata that you can filter on to look for specific kinds of activity. This can aid your diagnostic abilities.
+* **Con**: Logging generates data. While this enables the points above, you now have a new challenge of *managing* this new source of data.
+* **Con**: Log management is even more of a challenge as an application system grows. When you have multiple servers, where does all that log data go?
+
+This concept of logging is not a Django-specific idea. In fact, you can find a `logging` module in the Python standard library that serves as the basis for logging in the Python world. Django builds its logging features on top of the `logging` module.
+
+The short version of logging in Django is that you can use logging by configuring the `LOGGING` settings in your Django settings module. This process is described in the documentation at {{< extlink "https://docs.djangoproject.com/en/4.1/howto/logging/" "How to configure and use logging" >}}.
+
+In a basic workflow, logging can be little more than writing lines of data to a log *file*. If you're running an app on a VPS like Digital Ocean and have control over your filesystem, this is a good starting option. If you choose that path, keep in mind that disk space is a limited resource. One way to tank your app is to fill your machine's disk with logging data. To prevent this, you should investigate how to use logrotate. {{< extlink "https://linux.die.net/man/8/logrotate" "logrotate" >}} is a command that can archive your log data on whatever frequency you desire and clean up old data to prevent your storage from filling completely.
+
+You may follow a path like I've recommended frequently and deploy your app on a Platform as a Service (PaaS) like Heroku. In that kind of environment, you have less control over the tools that are available to run your application. In the case of Heroku, the platform does not guarantee that your app will stay on the same machine. Because of that constraint, you can't rely on a log file on the machine. Instead, you would need to add an additional service to the application that will store logs for your later use. {{< extlink "https://www.papertrail.com/" "Papertrail" >}} is an example of a log aggregation service that works with Heroku.
+
+As you can see, logging brings some complexity to manage if you choose to use it. Logs can be a great tool to understand what happened when a user used your app, especially if there was an error that didn't raise an exception. For me, personally, I *don't* reach for logging right away. While logging can be useful, it can be an overwhelming amount of data that can turn debugging into a "needle in a haystack" problem. Logging is still a potentially useful tool for your toolbox as you think about how to debug your apps.
## Summary
@@ -625,20 +215,15 @@ In this article,
{{< book >}}
In this chapter,
{{< /book >}}
-we saw debugging tips, tools, and techniques
-to make you a bug crushing machine
-in Django.
+we saw debugging tips, tools, and techniques to make you a bug crushing machine in Django.
+
We discussed:
* A systematic method for finding and fixing bugs in your app
-* How `print` is an awesome debugging tool
- that should be used without shame
-* Debugging with a "proper" debugging tool
- for particularly thorny problems
-* Using the source code of packages you didn't write
- to figure out what's going on
-* Logging and the ability to see a history
- of activity in your application
+* How `print` is an awesome debugging tool that should be used without shame
+* Debugging with a "proper" debugging tool for particularly thorny problems
+* Using the source code of packages you didn't write to figure out what's going on
+* Logging and the ability to see a history of activity in your application
{{< web >}}
That's the end of this series!
@@ -660,9 +245,7 @@ even after twenty articles,
{{< book >}}
even after all these chapters,
{{< /book >}}
-I've not covered everything.
-But this is the end
-of the line for me.
+I've not covered everything. But this is the end of the line for me.
{{< web >}}
I knew when I started this series
@@ -670,20 +253,14 @@ I knew when I started this series
{{< book >}}
I knew when I started this book
{{< /book >}}
-that I was going to cover a huge number
-of topics.
-What I didn't know when I started this in January of 2020
-is how wacky the world would be.
+that I was going to cover a huge number of topics. What I didn't know when I started this in January of 2020 is how wacky the world would be.
{{< web >}}
I thought that I might produce an article every other week
{{< /web >}}
{{< book >}}
I thought that I might produce a chapter every other week
{{< /book >}}
-and be done in less than a year.
-*How wrong I was!*
-More than two years later,
-I'm writing the words
+and be done in less than a year. *How wrong I was!* More than two years later, I'm writing the words
{{< web >}}
of this last article.
{{< /web >}}
@@ -697,29 +274,9 @@ As I wrap up this series,
{{< book >}}
As I wrap up this book,
{{< /book >}}
-my hope is that you,
-dear reader,
-have come to understand Django better.
-Django is a web framework written
-by people like you and me.
-That community,
-through years of collaboration,
-polished a tool
-that can bring your ideas
-to life on the web.
-
-While the framework might initially seem magical
-in all that it is able to do,
-we can see through study
-and examination
-that the code is comprehensible.
-The magic becomes less magical,
-but we can grow
-in our respect
-for those who contributed untold hours
-to making something so useful
-for others
-(for *free* in most cases!).
+my hope is that you, dear reader, have come to understand Django better. Django is a web framework written by people like you and me. That community, through years of collaboration, polished a tool that can bring your ideas to life on the web.
+
+While the framework might initially seem magical in all that it is able to do, we can see through study and examination that the code is comprehensible. The magic becomes less magical, but we can grow in our respect for those who contributed untold hours to making something so useful for others (for *free* in most cases!).
{{< web >}}
I would like to conclude this series
@@ -727,22 +284,14 @@ I would like to conclude this series
{{< book >}}
I would like to conclude this book
{{< /book >}}
-by thanking all of you readers out there.
-Along this lengthy journey,
-so many of you have reached out
+by thanking all of you readers out there. Along this lengthy journey, so many of you have reached out
{{< web >}}
and told me how this series has helped you grow
{{< /web >}}
{{< book >}}
and told me how this book has helped you grow
{{< /book >}}
-as a Django developer.
-I'm hopeful that developers have used these words
-to learn and create websites
-that help their own communities.
-Knowing that I've impacted some of you
-and, by extension, the communities
-that you've helped,
+as a Django developer. I'm hopeful that developers have used these words to learn and create websites that help their own communities. Knowing that I've impacted some of you and, by extension, the communities that you've helped,
{{< web >}}
made writing this series worth it.
{{< /web >}}
@@ -753,15 +302,6 @@ made writing this book worth it.
> Thank you for reading!
{{< web >}}
-If you'd like to see more content
-like this series,
-please feel free to sign up
-for my newsletter
-where I publish all new things first.
-If you have other questions,
-you can reach me online
-on Twitter
-where I am
-{{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
+If you'd like to see more content like this series, please feel free to sign up for my newsletter where I publish all new things first. If you have other questions, you can reach me online on Twitter where I am {{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
{{< /web >}}
From bfff4839dfb219de234a8b24ffa962aa211293b2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nazar=C3=A9=20da=20Piedade?=
Date: Fri, 9 Jun 2023 06:50:44 +0100
Subject: [PATCH 22/30] docs(pt): translate
`understand-django/urls-lead-way.pt.md`
---
.../2020-01-22-urls-lead-way.pt.md | 723 ++++--------------
1 file changed, 148 insertions(+), 575 deletions(-)
diff --git a/content/understand-django/2020-01-22-urls-lead-way.pt.md b/content/understand-django/2020-01-22-urls-lead-way.pt.md
index 8c7b02d8..730c3c2f 100644
--- a/content/understand-django/2020-01-22-urls-lead-way.pt.md
+++ b/content/understand-django/2020-01-22-urls-lead-way.pt.md
@@ -1,14 +1,7 @@
---
-title: "URLs Lead The Way"
+title: "URLs Guiam o Caminho"
description: >-
- How does a Django site know
- where to send requests?
- You have to tell it!
- In this next article
- in the Understand Django series,
- we look at URLs
- and how to let your users get
- to the right place.
+ Como uma aplicação de Django sabe onde enviar as requisições? Tu tens de dizê-la! Neste próximo artigo na série Entendendo a Django, olhamos as URLs e como ajudar os teus utilizadores a irem ao local correto.
image: img/django.png
type: post
categories:
@@ -21,79 +14,29 @@ tags:
---
{{< web >}}
-In the last article
-in the
-[Understand Django]({{< ref "/understand-django/_index.md" >}})
-series,
-we saw how a user's browser request goes
-from their browser
-to Django's "front door."
+No artigo anterior na série [Entendendo a Django]({{< ref "/understand-django/_index.pt.md" >}}), nós vimos como uma requisição de navegador do utilizador sai do navegador para a "porta da frente" da Django.
{{< /web >}}
-Now it's time to look
+Agora é hora de olhar
{{< web >}}
-at how Django processes those requests.
+em como a Django processa estas requisições.
{{< /web >}}
{{< book >}}
-at how Django processes a user's browser requests.
+em como a Django processa uma requisição de navegador do utilizador.
{{< /book >}}
-An HTTP request coming
-from a browser
-includes a URL describing which resource Django should produce.
-Since URLs can come
-in many forms,
-we must instruct Django
-on the kinds
-of URLs
-that our web application can handle.
-This is what the *URL configuration* is for.
-In the Django documentation,
-the URL configuration is called a URLconf,
-for short.
-
-Where is the URLconf?
-The URLconf is at the module path set
-by the `ROOT_URLCONF` setting
-in your project's settings file.
-If you ran the `startproject` command,
-then that setting will be named
-like `project.urls`
-where "project" is the name given
-as an argument
-to the command.
-In other words,
-the URLconf is placed
-in `project/urls.py`,
-right next to the `settings.py` file.
-
-That explains where the file resides,
-but it doesn't tell us much
-about how it works.
-Let's dig in more.
-
-{{< understand-django-series "urls" >}}
-
-## URLconf In Action
-
-Try to think
-of the URL configuration
-as a list
-of URL paths
-that Django will attempt to match
-from top to bottom.
-When Django finds a matching path,
-the HTTP request will route
-to a chunk of Python code
-that is associated with that path.
-That "chunk of Python code" is called a *view*
-which we will explore more
-in a bit.
-For the moment,
-trust that views know how
-to handle HTTP requests.
-
-We can use an example URLconf
-to bring this to life.
+Uma requisição de HTTP proveniente de um navegador inclui uma URL descrevendo qual recurso a Django deveria produzir. Já que as URLs podem chegar de várias formas, devemos instruir a Django sobre os tipos de URLs que a nossa aplicação de web pode manipular. É para isto que a *configuração da URL* serve. Na documentação da Django, a configuração da URL é chamada de URLconf, para abreviar.
+
+Onde está a URLconf? A URLconf está no caminho do módulo definido pela definição `ROOT_URLCONF` no ficheiro de definições do teu projeto. Se executaste o comando `startproject`, então esta definição será nomeada como `project.urls` onde "project" é o nome dado como um argumento para o comando. Em outras palavras, a URLconf é colocada em `project/urls.py`, exatamente próximo do ficheiro `settings.py`.
+
+Este explica onde o ficheiro reside, mas não diz-nos muito sobre como funciona. Vamos entrincheirar-nos mais.
+
+{{< understand-django-series-pt "urls" >}}
+
+## URLconf Em Ação
+
+Tente pensar da configuração da URL como uma lista de caminhos de URL que a Django tentará corresponder de alto a baixo. Quando a Django encontra um caminho correspondente, a requisição de HTTP enviará para um pedaço de código de Python que está associado com aquele caminho. Este "pedaço de código de Python" é chamado de uma *visão* que exploraremos mais um pouco. Para o momento, confia que as visões sabem como lidar com as requisições de HTTP.
+
+Nós podemos usar um exemplo de URLconf para trazer isto a vida:
```python
# project/urls.py
@@ -109,81 +52,20 @@ urlpatterns = [
]
```
-What's here matches well
-with what I described above:
-a list of URL paths
-that Django will try to match
-from top to bottom.
-The key aspect of this list is the name `urlpatterns`.
-Django will treat the list
-in a `urlpatterns` variable
-as the URLconf.
-
-The order of this list is also important
-because Django will stop scanning the list
-as soon as it encounters a match.
-The example doesn't show any conflict
-between paths,
-but it's possible to create two different `path` entries
-that can match the same URL
-that a user submits.
-I'll show an example
-of how that can happen
-after we see another aspect
-of paths.
-
-We can work through an example
-to see how this would work
-for `www.example.com`.
-When considering a URL
-in a URLconf,
-Django ignores the scheme (`https://`),
-the domain (`www.example.com`),
-and the leading slash
-for matching.
-Everything else is what the URLconf will match against.
-
-* A request to `https://www.example.com/about/` will look
- like `"about/"`
- to the pattern matching process
- and match the second `path`.
- That request would route
- to the `views.about` view.
-* A request to `https://www.example.com/` will look
- like `""`
- to the pattern matching process
- and match the first `path`.
- That request would route
- to the `views.home` view.
-
-> Aside:
-You might notice
-that Django URLs end
-with a slash character.
-This behavior is because
-of a Django {{< extlink "https://docs.djangoproject.com/en/4.1/misc/design-philosophies/#definitive-urls" "design philosophy" >}} choice.
-In fact,
-if you attempt to reach a URL
-like `https://www.example.com/about`,
-Django will redirect the request
-to the same URL
-with the slash appended
-because of the `APPEND_SLASH`
-{{< extlink "https://docs.djangoproject.com/en/4.1/ref/settings/#append-slash" "default setting" >}}.
-
-## The `path` Before Us
-
-The string part of `path`
-(e.g., `"about/"`) is called the *route*.
-A route can be a plain string
-as you've seen,
-but it can include other special structures
-with a feature called *converters*.
-When you use a converter,
-you can extract information out
-of a URL
-that a view can use later.
-Consider a path like this:
+O que está aqui corresponde bem com o que descrevi acima: uma lista de caminhos de URL que a Django tentará corresponder de alto a baixo. O conceito chave desta lista é o nome `urlpatterns` ou padrões de URLs. A Django tratará a lista numa variável `urlpatterns` como URLconf.
+
+A ordem desta lista também é importante porque a Django parará de examinar a lista assim que encontrar uma correspondência. O exemplo não mostra qualquer conflito entre os caminhos, mas é possível criar duas entradas de `path` diferentes que podem corresponder a mesma URL que o utilizador submete. Mostrarei um exemplo de como isto acontece depois de vermos um outro aspeto dos caminhos.
+
+Nós podemos trabalhar através de um exemplo para vermos como isto funcionaria para `www.example.com`. Onde consideramos que uma URL numa URLconf, a Django ignora o esquema (`https://`), o domínio (`www.example.com`), e o barra principal para correspondência. Todo o resto é aquilo que a URLConf comparará.
+
+* Uma requisição para `https://www.example.com/about/` estará como `"about/"` para o processo de correspondência de padrão e corresponderá o segundo `path`. Esta requisição enviaria para visão `views.about`.
+* Uma requisição para `https://www.example.com/` estará como `""` para o processo de correspondência de padrão e corresponderá o primeiro `path`. Esta requisição enviaria para visão `views.home`.
+
+> À parte: Tu podes notar que as URLs da Django terminam com um carácter de barra. Este comportamento é por causa de uma escolha da {{< extlink "https://docs.djangoproject.com/en/4.1/misc/design-philosophies/#definitive-urls" "filosofia de desenho" >}} da Django. De fato, se tentares alcançar uma URL como `https://www.example.com/about`, a Django redirecionará a requisição para a mesma URL com a barra anexada por causa da {{< extlink "https://docs.djangoproject.com/en/4.1/ref/settings/#append-slash" "definição padrão" >}} de `APPEND_SLASH`.
+
+## O `path` Diante de Nós
+
+A parte da sequência de caracteres do `path` (por exemplo, `"about/"`) é chamada de *rota*. Uma rota pode ser uma sequência de caracteres simples como tens visto, mas pode incluir outras estruturas especiais com uma funcionalidade chamada de *conversores*. Quando usares um conversor, podes extrair informação de uma URL que uma visão pode usar depois. Considere um caminho como este:
```python
path(
@@ -192,39 +74,23 @@ Consider a path like this:
),
```
-The two converters in this path are:
+Os dois conversores neste caminho são:
* ``
* ``
-The use of angle brackets
-and some {{< extlink "https://docs.djangoproject.com/en/4.1/topics/http/urls/#path-converters" "reserved names" >}}
-cause Django to perform extra parsing
-on a URL.
-Each converter has some expected rules to follow.
-
-* The `int` converter must match an integer.
-* The `slug` converter must match a slug.
- Slug is a bit of newspaper lingo
- that appears in Django
- because Django started
- as a project
- out of a newspaper in Kansas.
- A slug is a string that can include characters, numbers, dashes, and underscores.
-
-Given those converter definitions,
-let's compare against some URLs!
-
-* `https://www.example.com/blog/2020/urls-lead-way/` - MATCH!
-* `https://www.example.com/blog/twenty-twenty/urls-lead-way/` - NOPE.
-* `https://www.example.com/blog/0/life-in-rome/` - MATCH!
- Uh, maybe not what we wanted though.
- Let's look at that soon.
-
-Now we can revisit our ordering problem
-from earlier.
-Consider these two paths
-in different orders:
+O uso de parêntesis angulares e alguns {{< extlink "https://docs.djangoproject.com/en/4.1/topics/http/urls/#path-converters" "nomes reservados" >}} fazem a Django realizar analise adicional numa URL. Cada conversor tem algumas regras esperadas a seguir.
+
+* O conversor de `int` deve corresponder um inteiro.
+* O conversor de `slug` deve corresponder uma lesma. Lesma é um pouco do linguajar de jornal que aparece na Django porque a Django começou como um projeto devido a um jornal no Kansas. Uma lesma é uma sequência de caracteres que podem incluir caracteres, números, travessões, e sublinhados.
+
+Dado estas definições de conversor, vamos comparar contra algumas URLs!
+
+* `https://www.example.com/blog/2020/urls-lead-way/` - CORRESPONDE!
+* `https://www.example.com/blog/twenty-twenty/urls-lead-way/` - NÃO.
+* `https://www.example.com/blog/0/life-in-rome/` - CORRESPONDE! Uh, embora talvez não o que queríamos. Veremos isto em breve.
+
+Agora podemos revisar o nosso problema de ordenação de mais de mais cedo. Considere estes dois caminhos em ordens diferentes:
```python
path(
@@ -248,52 +114,27 @@ in different orders:
),
```
-In the first ordering,
-the converter will match any integer following `blog/`,
-including `https://www.example.com/blog/2020/`.
-That means that the first ordering will never call the `blog_for_twenty_twenty` view
-because Django matches `path` entries in order.
-
-Conversely,
-in the second ordering,
-`blog/2020/` will route to `blog_for_twenty_twenty` properly
-because it is matched first.
-That means there's a lesson
-to remember here:
+Na primeira ordem, o conversor corresponderá qualquer inteiro seguindo `blog/`, incluindo `https://www.example.com/blog/2020/`. O que significa que a primeira ordem nunca chamará a visão `blog_for_twenty_twenty` porque a Django corresponde as entradas de `path` na ordem. Inversamente, na segunda ordem, `blog/2020/` enviará para `blog_for_twenty_twenty` corretamente porque é correspondida primeiro. Isto significa que existe um lição a lembrar aqui:
{{< web >}}
-> When including `path` entries that match
- on ranges of values
- with converters (like the years example above),
- be sure to put them **after** the more specific entries.
+> Quando incluíres entradas de `path` para corresponder sobre gamas de valores com conversores (como o exemplo de anos acima), certifica-te de colocá-los **depois** das entradas mais específicas.
{{< /web >}}
{{< book >}}
-When including `path` entries that match
- on ranges of values
- with converters (like the years example above),
- be sure to put them **after** the more specific entries.
+Quando incluíres entradas de `path` para corresponder sobre gamas de valores com conversores (como o exemplo de anos acima), certifica-te de colocá-los **depois** das entradas mais específicas.
{{< /book >}}
-## An Abbreviated View Of Views
+## Uma Visão Abreviada de Visões
-What do converters do with this extra data?
-That's hard to explain
-without touching on views.
+O que os conversores fazem com esta dado adicional? É difícil de explicar sem tocar nas visões.
{{< web >}}
-The next article will cover views
+No próximo artigo cobriremos as visões
{{< /web >}}
{{< book >}}
-The next chapter will cover views
+No próximo capitulo cobriremos as visões
{{< /book >}}
-in far more depth,
-but here's a primer.
+em mais profundidade, mas cá está um compêndio.
-A view is code
-that takes a request
-and returns a response.
-Using Python's optional type hinting,
-here's an example
-that will send a `Hello World` response.
+Uma visão é o código que recebe uma requisição e retorna uma resposta. Usando a sugestão de tipo opcional da Python cá está um exemplo que enviará uma resposta `Hello World`:
```python
from django.http import (
@@ -307,16 +148,9 @@ def some_view(
return HttpResponse('Hello World')
```
-The `HttpRequest` is Django's translated format
-of an HTTP request
-wrapped up in a convenient container class.
-Likewise, `HttpResponse` is what we can use
-so that Django will translate our response data
-into a properly formatted HTTP response
-that will be sent back to the user's browser.
+O `HttpRequest` é o formato traduzido da Django de uma requisição de HTTP envolvida numa classe contentora conveniente. Do mesmo modo, `HttpResponse` é o que podemos usar para que a Django traduzir os dados da nossa resposta numa resposta de HTTP formatada corretamente que será enviada de volta para o navegador do utilizador.
-Now we can look
-at one of the converters again.
+Agora podemos novamente ver um dos conversores:
```python
path(
@@ -325,83 +159,35 @@ at one of the converters again.
),
```
-With this converter in place
-in the route,
-what would `blog_by_year` look like?
+Com este conversor no lugar na rota, com o que `blog_by_year` se pareceria?
```python
# application/views.py
from django.http import HttpResponse
def blog_by_year(request, year):
- # ... some code to handle the year
+ # ... algum código para manipular o ano
data = 'Some data set by code above'
return HttpResponse(data)
```
-Django begins to reveal some nice qualities here!
-The converter did a bunch
-of tedious work
-for us.
-The `year` argument set
-by Django
-will already be an integer
-because Django did the string parsing
-and conversion.
-
-If someone submits `/blog/not_a_number/`,
-Django will return a Not Found response
-because `not_a_number` can't be an integer.
-The benefit of this
-is that we don't have to put extra checking logic
-in `blog_by_year`
-to handle the weird case where `year` doesn't look like a number.
-That kind of feature is a real time saver!
-It keeps your code cleaner
-*and* makes handling more precise.
-
-What about that other strange example
-that we saw earlier
-of `/blog/0/life-in-rome/`?
-That would match our pattern
-from the earlier section,
-but let's assume we want to match a four digit year.
-How can we do that?
-We can use regular expressions.
-
-## Regular Expression Paths
-
-Regular expressions are a programming feature
-often likened to a chainsaw:
-*they are incredibly powerful,
-but you can cut off your foot
-if you're not careful.*
-
-Regular expressions can express complex patterns
-of characters
-in a concise way.
-This conciseness often gives regular expressions a bad reputation
-of being difficult to understand.
-When used carefully, though,
-they can be highly effective.
-
-A regular expression
-(which is often abbreviated to "regex")
-matches complex patterns
-in strings.
-This sounds exactly like our blog year problem!
-In our problem,
-we want to match a four digit integer only.
-Let's look at a solution
-that Django can handle
-and then break down what it means.
-
-As a reminder,
-this solution will match some URL path
-like `blog/2020/urls-lead-way/`.
-
-Note, we use the `re_path()` function for
-regular expression matching here, instead of `path()`.
+A Django começa a revelar algumas qualidades refinadas aqui! O conversor fez uma quantidade de trabalho tedioso por nós. O argumento `year` definido pela Django já será um inteiro porque a Django fez a analise e a conversão da sequência de caracteres.
+
+Se alguém submeter `/blog/not_a_number/`, a Django retornará uma resposta "Não Encontrada" porque `not_a_number` não pode ser um inteiro. O benefício disto é que não temos de colocar lógica de verificação adicional no `blog_by_year` para lidar com o caso estranho onde `year` não parece ser um número. Este tipo de funcionalidade é um verdadeiro economizador de tempo! Ele mantém o teu código mais limpo *e* torna a manipulação mais precisa.
+
+E aquele outro exemplo estranho que vimos mais cedo de `/blog/0/life-in-rome/`? Aquele corresponderia ao nosso padrão da seção anterior, mas vamos presumir que queremos corresponder um ano de quatro dígitos. Como podemos fazer isto? Nós podemos usar expressões regulares.
+
+## Caminhos de Expressão Regular
+
+As expressões regulares são uma característica da programação frequentemente ligadas à uma motosserra: *elas são incrivelmente poderosas, mas podes cortar o teu pé se não fores cuidadoso.*
+
+As expressões regulares podem expressar padrões complexos de caracteres duma maneira concisa. Esta concisão muitas vezes dá as expressões regulares uma má reputação de ser difícil de entender. Quando usadas cuidadosamente, podem ser altamente efetivas.
+
+Uma expressão regular (que é frequentemente abreviada para "regex") corresponde padrões complexos em sequências de caracteres. Isto soa exatamente como o nosso problema do ano do blogue! No nosso problema, queremos corresponder apenas um inteiro de quatro dígitos. Vamos olhar uma solução que a Django pode manipular e decompor o que importa.
+
+Como um resto, esta solução corresponderá algum caminho de URL como `blog/2020/urls-lead-way/`.
+
+Nota que, usamos a função `re_path()` para a correspondência de expressão regular, ao invés de `path()`:
```python
re_path(
@@ -410,113 +196,37 @@ re_path(
),
```
-This crazy string behaves exactly like our earlier example
-**except** that it is more precise
-about only allowing four digit years.
-The crazy string also has a name.
-It is called a *regex pattern*.
-When the Django code runs,
-it will test URL paths against the rules
-that are defined in this pattern.
-
-To see how it works,
-we have to know what the parts of the pattern mean.
-We can explain this pattern one chunk
-at a time.
-
-* The string itself starts with `r"`
- because it is a raw string in Python.
- This is used because regular expressions use `\` extensively.
- Without a raw string,
- a developer would have to escape the backslash repeatedly
- by using `\\`.
-* The caret, `^`, means "the pattern must *start* here."
- Because of the caret,
- a path that starts like `myblog/...` will not work.
-* `blog/` is a literal interpretation.
- Those characters must match exactly.
-* The portion inside parentheses `(?P[0-9]{4})` is a *capture group*.
- The `?P` is the name to associate
- with the capture group and is similar
- to the right side of the colon
- in a converter like ``.
- The name allows Django
- to pass on the content
- in an argument called `year`
- to the view.
- The other part of the capture group, `[0-9]{4}`,
- is what the pattern is actually matching.
- `[0-9]` is a *character class*
- which means "match any number from 0 through 9."
- The `{4}` means that it must match **exactly** four times.
- This is the specificity that `re_path` gives
- that the `int` converter could not!
-* The slash, `/`, between capture groups is another literal character to match.
-* The second capture group, `(?P[\w-]+)`, will put whatever it matches
- into an argument named `slug`.
- The character class of `[\w-]` contains two types
- of characters. `\w` means any word character
- that you might have in a natural language
- and digits and underscores.
- The other type of character is a literal dash, `-`, character.
- Finally, the plus, `+`, character means
- that the character class must match 1 or more times.
-* The last slash is also a literal character match.
-* To complete the pattern,
- the dollar sign, `$`, acts like the opposite
- of the caret and means
- "the pattern must *end* here."
- Thus, `blog/2020/some-slug/another-slug/` will not match.
-
-Note that you cannot mix the `path` style and `re_path` style strings.
-The example above had to describe the slug as a regular expression
-instead of using the slug converter (i.e., ``).
-
-Congratulations!
-This is definitely the hardest section
+Esta sequência de caracteres doida comporta-se exatamente como o nosso exemplo anterior **exceto** que é mais precisa sobre apenas permitir anos de quatro dígitos. A sequência de caracteres doida também tem um nome. É chamada de *padrão de expressão regular*. Quando o código da Django executa, testará os caminhos de URL contra as regras que são definidas neste padrão.
+
+Para vermos como isto funciona, temos de saber o que as partes do padrão significam. Nós podemos explicar este padrão um pedaço de cada vez:
+
+* A própria sequência de caracteres começa com `r"` porque é uma sequência de caracteres crua em Python. Esta é usada porque as expressões regulares usam `\` extensivamente. Sem uma sequência de caracteres crua, um programador teria de escapar a barra obliqua invertida usando `\\`.
+* O circunflexo, `^`, significa que "o padrão deve *começar* aqui". Por causa do circunflexo, um caminho que começa como `myblog/...` não funcionará.
+* `blog/` é uma interpretação literal. Estes caracteres devem corresponder exatamente.
+* A porção dentro dos parênteses `(?P[0-9]{4})` é um *grupo de captura*. A `?P` é o nome associado com o grupo de captura e é parecido com o lado direito dos dois pontos num conversor como ``. O nome permite a Django passar o conteúdo num argumento chamado `year` para a visão. A outra parte do grupo de captura, `[0-9]{4}`, é o que o padrão está de fato a corresponder. `[0-9]` é uma *classe de carácter* que significa "corresponde qualquer número de 0 à 9". O `{4}` significa que deve corresponder **exatamente** quatro vezes. Isto é a especificidade que `re_path` dá que o conversor de `int` não poderia!
+* A barra, `/`, entre os grupos de captura é um outro carácter literal a corresponder.
+* O segundo grupo de captura, `(?P[\w-]+)`, colocará tudo aquilo que corresponder num argumento nomeado `slug`. A classe de carácter de `[\w-]` contém dois tipos de caracteres. `\w` significa que qualquer carácter que podes ter numa linguagem natural e dígitos e sublinhados. O outro tipo de carácter é um carácter de travessão literal, `-`. Finalmente, o carácter mais, `+`, significa que a classe de carácter deve corresponder 1 ou mais vezes.
+* A última barra também é uma correspondência de carácter literal.
+* Para completar o padrão, o sinal de dólar, `$`, age como o oposto do circunflexo e significa que "o padrão deve *terminar* aqui". Assim, `blog/2020/some-slug/another-slug/` não corresponderá.
+
+Nota que não podes misturar as sequências de caracteres de estilo de `path` e `re_path`. O exemplo acima tinha que descrever a lesma como uma expressão regular ao invés de usar o conversor de lesma (por exemplo, ``).
+
+Parabéns! Esta é definitivamente a seção mais difícil
{{< web >}}
-of this article.
+deste artigo.
{{< /web >}}
{{< book >}}
-of this chapter.
+deste capítulo.
{{< /book >}}
-If you understood what we did
-with `re_path`,
-the rest of this should feel very comfortable.
-If not,
-*please don't fret about it!*
-If you want to know more about regular expressions,
-know that everything I described
-in the pattern
-is *not* Django specific.
-Instead,
-this is Python's built-in behavior.
-You can learn more
-about regular expressions
-from Python's {{< extlink "https://docs.python.org/3/howto/regex.html" "Regular Expression HOWTO" >}}.
-
-Knowing that this power
-with `re_path`
-is there may help you later
-on your Django journey,
-even if you don't need it now.
-
-## Grouping Related URLs
-
-Up to this point,
-we've looked at individual routes
-that you can map
-in a URLconf.
-What can we do
-when a related group
-of views
-should share a common path?
-Why would we want to do this?
-
-Let's imagine you're building an educational project.
-In your project,
-you have schools, students, and other education related concepts.
-You *could* do something like:
+Se entendeste o que fizemos com a `re_path`, o resto disto deveria ser mais confortável. Se não, *não te preocupes com isto!* Se quiseres saber mais sobre as expressões regulares, saiba que tudo que descrevi no padrão *não* é específico da Django. Ao invés disto, isto é o comportamento embutido da Python. Tu podes aprender mais sobre as expressões regularas a partir da {{< extlink "https://docs.python.org/3/howto/regex.html" "HOWTO de Expressão Regular" >}}.
+
+Saber que este poder com `re_path` existe pode ajudar-te depois na tua jornada da Django, mesmo se não precisares dele agora.
+
+## Agrupando URLs Relacionadas
+
+Até este momento, olhávamos em rotas individuais que podes mapear numa configuração de URL ou `URLconf`. O que podes fazer quando um grupo relacionado de visões deveriam partilhar um caminho comum? Porquê quereríamos fazer isto?
+
+Imaginemos que estás a construir um projeto educacional. No teu projeto, tens escolas, estudantes, e outros conceitos relacionados a educação. Tu *poderias* fazer algo como:
```python
# project/urls.py
@@ -548,13 +258,7 @@ urlpatterns = [
]
```
-This approach would work fine,
-but it forces the root URLconf
-to know about all the views defined
-in each app, `schools` and `students`.
-Instead,
-we can use `include`
-to handle this better.
+Esta abordagem funcionaria bem, mas força a configuração de URL raiz conhecer todas as visões definidas em cada aplicação, `schools` e `students`. Ao invés disto, podemos usar `include` para manipular isto melhor:
```python
# project/urls.py
@@ -572,9 +276,7 @@ urlpatterns = [
]
```
-Then,
-in each application,
-we would have something like:
+Então, em cada aplicação, poderíamos ter algo como:
```python
# schools/urls.py
@@ -591,36 +293,13 @@ urlpatterns = [
]
```
-The use of `include` gives each Django app autonomy
-in what views it needs to define.
-The project can be blissfully "ignorant"
-of what the application is doing.
-
-Additionally,
-the repetition of `schools/` or `students/` is removed
-from the first example.
-As Django processes a route,
-it will match
-on the first portion
-of the route
-and pass the *remainder*
-onto the URLconf
-that is defined in the individual app.
-In this way,
-URL configurations can form a tree
-where the root URLconf is where all requests start,
-but individual applications can handle the details
-as a request is routed to the proper app.
-
-## Naming URLs
-
-We've looked at the main ways
-that URLs get defined
-with `path`, `re_path`, and `include`.
-There is another aspect to consider.
-How can we refer to URLs
-in other places in the code?
-Consider this (rather silly) view:
+O uso da `include` dá a cada aplicação da Django autonomia em quais visões precisa definir. O projeto pode ser completamente "ignorante" do que a aplicação está a fazer.
+
+Além disso, a repetição de `schools/` ou `students/` é removida do primeiro exemplo. Já que a Django processa uma rota, corresponderá a primeira porção da rota e passará o *resto* para a configuração de URL que for definida na aplicação individual. Deste modo, as configurações de URL podem manipular a árvore onde a configuração de URL raiz está onde todas as requisições começam, mas as aplicações individuais podem manipular os detalhes visto que uma requisição é enviada para a aplicação apropriada.
+
+## Nomeando URLs
+
+Vimos as principais maneiras que as URLs são definidas com `path`, `re_path`, e `include`. Existe um outro aspeto a considerar. Como podemos fazer referência as URLs em outros lugares no código? Considere esta (um pouco disparatada) visão:
```python
# application/views.py
@@ -634,26 +313,7 @@ def old_blog_categories(request):
)
```
-A redirect is when a user tries to visit a page
-and is sent somewhere else
-by the browser.
-There are much better ways to handle redirects
-than this example shows,
-but this view illustrates a different point.
-What would happen if you want to restructure the project
-so that blog categories moved
-from `/blog/categories/`
-to `/marketing/blog/categories/`?
-In the current form,
-we would have to fix this view
-and any other view
-that referenced the route directly.
-
-What a waste of time!
-Django gives us tools to give paths names
-that are independent
-from the explicit route.
-We do this with the `name` keyword argument to `path`.
+Um redirecionamento é quando um utilizador tenta visitar uma página e é enviado para outro lugar pelo navegador. Existem maneiras muito melhores de manipular redirecionamentos do que este exemplo mostra, mas esta visão ilustra um ponto diferente. O que aconteceria se quiseres reestruturar o projeto para que as categorias do blogue sejam movidas de `/blog/categories/` para `/marketing/blog/categories/`? Na forma atual, teríamos de concertar esta visão e qualquer outra visão que fazia referência a rota diretamente. Mas que perda de tempo! A Django dá-nos ferramentas para dar nomes de caminhos que são independentes da rota explícita. Nós fizemos isto com o argumento de palavra-chave `name` para `path`:
```python
# project/urls.py
@@ -672,14 +332,7 @@ urlpatterns = [
]
```
-This gives us `blog_categories`
-as an independent name
-from the route
-of `/marketing/blog/categories/`.
-To use that name,
-we need `reverse`
-as its counterpart.
-Our modified view looks like:
+Isto dá-nos `blog_categories` como um nome independente da rota de `/marketing/blog/categories/`. Para usarmos este nome, precisamos de `reverse` como sua contraparte. A nossa visão modificada parece-se com:
```python
# application/views.py
@@ -694,83 +347,33 @@ def old_blog_categories(request):
)
```
-The job of `reverse`
-is to look up any path name
-and return its route equivalent.
-That means that:
+O trabalho da `reverse` é procurar qualquer nome de caminho e retornar a sua rota equivalente. Isto significa isto:
```python
reverse("blog_categories") == "/marketing/blog/categories/"
```
-At least until you want to change it again. 😁
+Pelo menos até quiseres mudá-la novamente. 😁
-## When Names Collide
+## Quando Nomes Colidem
-What happens
-if you have multiple URLs
-that you want to give the same `name`?
-For instance,
-`index` or `detail` are common names
-that you may want to apply.
-We can turn to
-{{< extlink "https://www.python.org/dev/peps/pep-0020/" "The Zen of Python" >}}
-for advice.
+O que acontece se tiveres várias URLs que queres dar o mesmo `name`? Por exemplo, `index` ou `detail` são nomes comuns que podes querer aplicar. Nós podemos recorrer ao {{< extlink "https://www.python.org/dev/peps/pep-0020/" "O Zen da Python" >}} por conselho.
-> The Zen of Python, by Tim Peters
+> O Zen da Python, por Tim Peters
>
-> Beautiful is better than ugly.
+> Belo é melhor do que feio.
>
> ...
>
-> **Namespaces are one honking great idea -- let's do more of those!**
-
-Namespaces might be new to you
-if you haven't been programming long.
-They are a *shared space for names*.
-Maybe that's clear,
-but I recall struggling
-with the concept
-when I first began to write software.
-
-To make an analogy
-to something in the real world,
-let's use trusty buckets.
-Imagine you have two red balls
-and two blue balls.
-Put one ball of each color
-in each of the two buckets labeled "A" and "B."
-If I wanted a specific blue ball,
-I can't say "please give me the blue ball"
-because that would be ambiguous.
-Instead,
-to get a specific ball,
-I would need to say "please give me the blue ball in bucket B."
-In this scenario,
-the bucket is the namespace.
-
-The example that we used for schools and students
-can help illustrate this idea
-in code.
-Both apps had an `index` view
-to represent the root
-of the respective portions of the project
-(i.e., `schools/` and `students/`).
-If we wanted to refer
-to those views,
-we'd try to pick the easiest choice
-of `index`.
-Unfortunately,
-if you pick `index`,
-then Django can't tell which one is the right view
-for `index`.
-The name is ambiguous.
-
-One solution is to create your own namespace
-by prefixing `name`
-with something common
-like `schools_`.
-The trouble with that approach is that the URLconf repeats itself.
+> **Os espaços de nome são uma ótima ideia -- façamos mais destes!**
+
+Os espaços de nome podem ser novos para ti se não tens estado a programar por muito tempo. Eles são um *espaço partilhado para nomes*. Talvez seja claro, mas eu lembro de ter dificuldades com o conceito quando comecei a escrever software.
+
+Para fazer uma analogia à algo no mundo real, usamos baldes de confiança. Imagine que tens duas bolas vermelhas e duas bolas azuis. Coloque uma bola de cada cor dentro de cada um dos dois baldes rotulados de "A" e "B". Se eu queria uma bola azul específica, não podia dizer "por favor dê-me a bola azul" porque isto seria ambíguo. Ao invés disto, para receber uma bola específica, precisaria de dizer "por favor dê-me a bola azul no balde B". Neste cenário, o balde é o espaço de nome.
+
+O exemplo que usamos para as escolas e estudantes pode ajudar a ilustrar esta ideia no código. Ambas aplicações tinham uma visão `index` para representar a raiz das respetivas porções do projeto (por exemplo, `schools` e `students`). Se queríamos fazer referência aquelas visões, tentaríamos escolher a opção mais fácil de `index`. Infelizmente, se escolheres a`index`, então a Django não pode dizer qual delas é a visão correta para `index`. O nome é ambíguo.
+
+Uma solução é criar o teu próprio espaço de nome prefixando o `name` com algo comum como `schools_`. O problema com esta abordagem é que a configuração de URL repete-se a si mesma:
```python
# schools/urls.py
@@ -792,8 +395,7 @@ urlpatterns = [
]
```
-Django provides an alternative
-that will let you keep a shorter name.
+A Django fornece uma alternativa que permitir-te-á manter um nome mais curto:
```python
# schools/urls.py
@@ -812,67 +414,38 @@ urlpatterns = [
]
```
-By adding `app_name`,
-we signal to Django
-that these views are in a namespace.
-Now when we want to get a URL,
-we use the namespace name
-and the URL name
-and join them
-with a colon.
+Ao adicionar `app_name`, assinalamos para a Django que estas visões estão num espaço de nome. Agora quando quisermos receber uma URL, usamos o espaço de nome e o nome da URL e os combinamos com um sinal de ponto e vírgula:
```python
reverse("schools:index") == "/schools/"
```
-This is another convenience
-that Django gives
-to make our application development experience easier.
+Esta é uma outra conveniência que a Django dá para tornar a nossa experiência de desenvolvimento de aplicação mais fácil.
-That brings us to a close
-on the subject of URLs.
-By now,
-we've seen how to:
+Isto traz-nos para o fim do assunto de URLs. Por agora, vimos como:
-* Make a URL configuration
- by making a module with a list of `urlpatterns`.
-* Create URLs with `path` and `re_path`.
-* Use converters to extract information for views.
-* Use regular expressions to express more complex URL data.
-* Group related URLs together with `include`.
-* Refer to a URL by its `name`.
-* Put related names together in a namespace.
+* Fazer uma configuração de URL criando um módulo com uma lista de `urlpatterns`.
+* Criar as URLs com `path` e `re_path`.
+* Usar conversores para extrair informação para as visões.
+* Usar expressões regulares para expressar dados de URL mais complexos.
+* Agrupar URLs relacionadas em conjunto com `include`.
+* Fazer referência à uma URL pelo seu `name`.
+* Colocar nomes relacionados juntos num espaço de nome.
{{< web >}}
-In the next article,
-we'll dig into views.
-This article only gave the briefest definition
+No próximo artigo, escavaremos as visões. Este artigo apenas deu a definição mais breve
{{< /web >}}
{{< book >}}
-In the next chapter,
-we'll dig into views.
-This chapter only gave the briefest definition
+No próximo capítulo, escavaremos as visões. Este capítulo apenas deu a definição mais breve
{{< /book >}}
-to what a view is.
-Django gives us very rich options
-when working with views.
-We're going to explore:
+para o que uma visão é. A Django dá-nos opções muito ricas quando trabalhamos com visões. Exploraremos:
-* View functions
-* View classes
-* Some built-in supporting views
-* Decorators that supercharge views.
+* Funções de visão
+* Classes de visão
+* Algumas visões de suporte embutidas
+* Decoradores que sobrealimentam as visões.
{{< web >}}
-If you'd like to follow along
-with the series,
-please feel free to sign up
-for my newsletter
-where I announce all of my new content.
-If you have other questions,
-you can reach me online
-on Twitter
-where I am
-{{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
+Se gostarias de seguir com a série, sinta-se livre para inscrever-se no meu boletim informativo onde anúncio todos os meus novos conteúdos. Se tiveres outras questões, podes contactar-me na Twitter onde sou {{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
{{< /web >}}
From 5d02566f72c595943a8aa965939c1381b17aa79a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nazar=C3=A9=20da=20Piedade?=
Date: Sun, 25 Jun 2023 09:36:58 +0100
Subject: [PATCH 23/30] docs(pt): translate
`understand-django/views-on-views.pt.md`
---
.../2020-03-03-views-on-views.pt.md | 274 +++++++++---------
1 file changed, 136 insertions(+), 138 deletions(-)
diff --git a/content/understand-django/2020-03-03-views-on-views.pt.md b/content/understand-django/2020-03-03-views-on-views.pt.md
index a951e0de..2cf79e47 100644
--- a/content/understand-django/2020-03-03-views-on-views.pt.md
+++ b/content/understand-django/2020-03-03-views-on-views.pt.md
@@ -1,7 +1,7 @@
---
-title: "Views On Views"
+title: "Visões Sobre Visões"
description: >-
- Django URLs expect to send a response back to a user. Where does that response come from? A Django view! This article looks into the fundamentals of views and how to use them in your project.
+ As URLs da Django esperam enviar uma resposta de volta para um utilizador. De onde vem esta resposta? Uma visão de Django! Este artigo investiga os fundamentos das visões e como usá-las no teu projeto.
image: img/django.png
type: post
categories:
@@ -15,31 +15,30 @@ tags:
---
{{< web >}}
-In the previous [Understand Django]({{< ref "/understand-django/_index.pt.md" >}}) article, I covered URLs and the variety of tools that Django gives us to describe the outside interface to the internet for your project. In this article,
+No artigo anterior do [Entendendo a Django]({{< ref "/understand-django/_index.pt.md" >}}), eu cobri as URLs e a variedade de ferramentas que a Django nos dá para descrever a interface no exterior para a internet para o teu projeto. Neste artigo,
{{< /web >}}
{{< book >}}
-Now that we have a grasp on URLs in Django,
+Agora que temos uma compreensão sobre as URLs na Django,
{{< /book >}}
-we'll examine the core building block that makes those URLs work: the Django view.
+examinaremos o bloco de construção principal que faz aquelas URLs funcionarem: a visão da Django.
{{< understand-django-series-pt "views" >}}
-## What Is A View?
+## O Que É Uma Visão?
-A view is a chunk of code that receives an HTTP request and returns an HTTP response. Views are where you use Django's core functionality: to respond to requests made to an application on the internet.
+Uma visão é um pedaço de código que recebe uma requisição de HTTP e retorna uma resposta de HTTP. As visões são onde usas a funcionalidade principal da Django: responder às requisições feitas para uma aplicação na internet.
-You might notice that I'm a bit vague about "chunk of code." That was deliberate. The reason is that views come in multiple forms. To say views are *functions* would be part of the story. Later chapters in that story cover how they can also be implemented in *classes*.
+Tu podes notar que sou um pouco vago sobre o "pedaço de código." Isto foi deliberado. A razão é que as visões entram de várias formas. Para dizer que visões são *funções* que seriam parte da história. Os capítulos posteriores desta história cobre como também podem ser implementadas em *classes*.
-Even if I attempted to call views *callables,* I still would not portray them accurately because of the ways that certain types of views get plugged into a Django app. For instance, a view based on a class will *produce* a callable as we'll see in a later section.
+Mesmo se tentasse chamar visões *chamáveis,* continuaria a não retratá-las com precisão por causa das maneiras que certos tipos de visões são ligados à uma aplicação de Django. Por exemplo, uma visão baseada numa classe *produzirá* uma chamável como veremos numa seção posterior.
-Let's start with functions since I think they are the gentlest introduction to views.
+Vamos começar com funções uma vez que penso serem a maneira mais gentil de introdução as visões.
-## Function Views
+## Visões de Função
-A function view is precisely that, a function. The function takes an `HttpRequest` instance as input and returns an `HttpResponse` (or one of its many subclasses)
-as output.
+Uma visão de função é precisamente isto, uma função. A função recebe uma instância de `HttpRequest` como entrada e retorna um `HttpResponse` (ou uma de suas muitas subclasses) como saída.
-The classic "Hello World" example would look like what is listed below:
+O clássico exemplo "Olá Mundo" se pareceria com o que está listado abaixo:
```python
# application/views.py
@@ -49,24 +48,24 @@ def hello_world(request):
return HttpResponse('Hello World')
```
-Adding the `hello_world` view to a URL configuration which we learned about
+Adicionando a visão `hello_word` à configuração de URL que aprendemos
{{< web >}}
-in the last article,
+no último artigo,
{{< /web >}}
{{< book >}}
-in the last chapter,
+no último capítulo,
{{< /book >}}
-you could visit a browser at the URL and find the text "Hello World" on your browser page.
+poderias visitar um navegador na URL e encontrar o texto "Hello World" na página do teu navegador.
-Maybe you don't find that very exciting, but I do, and I think you should! The framework did so much work for us, and *our* job is to write a mere couple of lines of Python. When plugged into a web server on the internet, your greeting can reach anyone with access to the net. That's staggering and is worth reflecting on.
+Talvez não aches isto muito entusiasmante, mas eu acho, e penso que deverias! A abstração fez tanto por nós, e o *nosso* trabalho é escrever algumas meras linhas de Python. Quando ligada à um servidor de Web na internet, a tua saudação pode alcançar alguém com acesso à internet. É espantoso e é digno de reflexão.
-Django does most of the heavy lifting for us. The raw HTTP request fits neatly into the `HttpRequest` class. Our example view doesn't use that information, but it's accessible if we need it. Likewise, we're not using much of `HttpResponse`. Still, it's doing all the work to ensure it appears on a user's browser and delivers our message.
+A Django faz a maioria do trabalho pesado por nós. A requisição de HTTP pura encaixa-se perfeitamente à classe `HttpRequest`. A nossa visão de exemplo não usa esta informação, mas está acessível se precisarmos dela. Da mesma maneira, não estamos a usar muito do `HttpResponse`. Mesmo assim, está a fazer todo o trabalho de garantir que aparece no navegador de um utilizador e entregar a nossa mensagem.
-To see what we can do with views, let's look closely at `HttpRequest` and `HttpResponse` to get a glimpse at what's going on.
+Para ver o que podemos fazer com as visões, olharemos com atenção no `HttpRequest` para obtermos um vislumbre do que está a acontecer.
## HttpRequest
-`HttpRequest` is a Python class. Instances of this class represent an HTTP request. HTTP is the transfer protocol that the internet uses to exchange information. A request can be in a variety of formats, but a standard request might look like:
+`HttpRequest` é uma classe de Python. As instâncias desta classe representam uma requisição de HTTP. HTTP é o protocolo de transferência que a internet usa para trocar informação. Uma requisição pode estar numa variedade de formatos, mas uma requisição padrão pode parecer-se com:
```http
POST /courses/0371addf-88f7-49e4-ac4d-3d50bb39c33a/edit/ HTTP/1.1
@@ -90,41 +89,41 @@ name=Science
```
{{< web >}}
-This example is from a side project that uses school data. I have trimmed some lines out of the request so it will fit better on the screen, and I did some slight reformatting to make the content a bit clearer.
+Este exemplo é dum projeto paralelo que usa dados escolares. Eu recortei algumas linhas da requisição assim enquadrar-se-á melhor no ecrã, e fiz alguma ligeira reformatação para tornar o conteúdo um pouco mais claro.
{{< /web >}}
-When Django receives a request like this, it will parse the data and store it in an `HttpRequest` instance. The request provides convenient access to all parts of the raw data with helpful attributes for the most commonly used parameters. When considering the example, the request would have:
+Quando a Django recebe uma requisição como esta, analisará os dados e o armazenará numa instância de `HttpRequest`. A requisição fornece acesso conveniente à todas as partes dos dados crus com atributos úteis para os parâmetros mais comummente usados. Quando consideras o exemplo, a requisição teria:
-* `method` - This matches the HTTP method of `POST` and can be used to act on the *kind* of request the user sent.
-* `content_type` - This attribute instructs Django on how to handle the data in the request. The example value would be `application/x-www-form-urlencoded ` to indicate that this is user-submitted form data.
-* `POST` - For POST requests, Django processes the form data and stores the data into a dictionary-like structure. `request.POST['name']` would be `Science` in our example.
-* `GET` - Anything added to the query string (i.e., the content after a `?` character such as `student=Matt` in `/courses/?student=Matt`) is stored in a dictionary-like attribute as well.
-* `headers` - This is where all the HTTP headers like `Host`, `Accept-Language`, and the others are stored. `headers` is also dictionary-like and can be accessed like `request.headers['Host']`.
+* `method` - Isto corresponde o método HTTP de `POST` e pode ser usado para agir sobre o *tipo* de requisição que o utilizador enviou.
+* `content_type` - Este atributo instrui a Django a como lidar com os dados na requisição. O valor de exemplo seria `application/x-www-form-urlencoded` para indicar que isto é um dado de formulário submetido pelo utilizador.
+* `POST` - Para requisição de publicação, a Django processa os dados de formulário e armazena os dados numa estrutura parecida com um dicionário. `request.POST['name']` seria `Science` no nosso exemplo.
+* `GET` - Tudo adicionado à sequência de caracteres de consulta (por exemplo, o conteúdo depois dum carácter `?` tal como `student=Matt` em `/courses/?student=Matt`) também é armazenado num atributo parecido com um dicionário.
+* `headers` - Isto é onde todos os cabeçalhos de HTTP como `Host`, `Accept-Language`, e outros são armazenados. `headers` também é parecido com dicionário e pode ser acessado como `request.headers['Host']`.
-Other attributes are available to `HttpRequest`, but that list will get you far enough to get started. Check out {{< extlink "https://docs.djangoproject.com/en/4.1/ref/request-response/" "Request and response objects" >}} for the other attributes.
+Os outros atributos estão disponíveis para o `HttpRequest`, ams aquela lista levar-te-á longe o suficiente para começares. Consulte {{< extlink "https://docs.djangoproject.com/en/4.1/ref/request-response/" "Objetos de requisição e resposta" >}} por outros atributos.
-I should also note that `HttpRequest` instances are a common place to attach extra data. Django requests pass through many pieces in the framework. This makes the objects great candidates for extra features that you may require. For instance, if you need user management
+Eu deveria também salientar que as instâncias de `HttpRequest` são um lugar comum para anexar dados adicionais. As requisição da Django passam por vários pedaços na abstração. Isto torna os objetos excelentes candidatos para funcionalidades adicionais que podes precisar. Por exemplo, se precisares de gestão de utilizador
{{< web >}}
-(which we will explore in a future article),
+(o que exploraremos num artigo do futuro),
{{< /web >}}
{{< book >}}
-(which we will explore in a future chapter),
+(o que exploraremos num capítulo do futuro),
{{< /book >}}
-there is code that can attach a `request.user` attribute to represent a user in your system. It's *very* handy.
+existe código que pode anexar um atributo `request.user` para representar um utilizador no teu sistema. É *muito* prático.
-You can think of `HttpRequest` objects as the common interface for most of the inputs that my code uses.
+Tu podes pensar dos objetos de `HttpRequest` como a interface comum para a maioria das entradas que o meu código usa.
## HttpResponse
-The other major interface that your views will use either directly or indirectly is the `HttpResponse` interface.
+A outra interface principal que as tuas visões usarão ou diretamente ou indiretamente é a interface `HttpResponse`.
-Your job as a Django user is to make your views return some kind of `HttpResponse`. A response instance will include all the necessary information to create a valid HTTP response for a user's browser.
+O teu trabalho como um utilizador da Django é fazer as tuas visões retornarem algum tipo de `HttpResponse`. Uma instância de resposta incluirá todos as informações necessárias para criar uma resposta de HTTP válida para um navegador do utilizador.
-Some of the common `HttpResponse` attributes include:
+Alguns dos atributos de `HttpResponse` comuns incluem:
-* `status_code` - This is the HTTP status code. Status codes are a set of numbers that HTTP defines to tell a client (e.g., a browser) about the success or failure of a request. `200` is the usual success code. Any number from `400` and up will indicate some error, like `404` when a requested resource is not found.
-* `content` - This is the content that you provide to the user. The response stores this data as bytes. If you supply Python string data, Django will encode it to bytes for you.
+* `status_code` - Isto é o código do estado do HTTP. Os códigos de estado são um conjunto de números que a HTTP define para dizer ao cliente (por exemplo, um navegador) sobre o sucesso ou fracasso duma requisição. `200` é o código de sucesso habitual. Qualquer número de `400` para cima indica algum erro, como `404` quando um recurso requisitado não foi encontrado.
+* `content` - Isto é conteúdo que forneces ao utilizador, A resposta armazena este dado como bytes. Se forneceres dado de sequências de caracteres de Python, a Django o codificará para bytes por ti.
```python
>>> from django.http import HttpResponse
@@ -133,26 +132,26 @@ Some of the common `HttpResponse` attributes include:
b'Hello World'
```
-When working with Django views, you won't always use `HttpResponse` directly. `HttpResponse` has a variety of subclasses for common uses. Let's look at some:
+Quando trabalhas com as visões da Django, não usarás sempre `HttpResponse`. `HttpResponse` tem uma variedade de subclasses para usos comuns. Vamos olhar alguns:
-* `HttpResponseRedirect` - You may want to send a user to a different page. Perhaps the user bought something on your site, and you would like them to see a receipt page of their order. This subclass is perfect for that scenario.
-* `HttpResponseNotFound` - This is the subclass used to create a `404 Not Found` response. Django provides some helper functions to return this so you might not use this subclass directly, but it's good to know it's available.
-* `HttpResponseForbidden` - This type of response can be used when you don't want a user to access a part of your website (i.e., HTTP status `403 Forbidden`).
+* `HttpResponseRedirect` - tu podes querer enviar um utilizador para uma página diferente. Talvez o utilizador comprou algo na tua página, e gostarias de enviá-lo para ver uma página de recibo da usa encomenda. Esta subclasse é perfeita para este cenário.
+* `HttpResponseNotFound` - Esta é a subclasse usada para criar uma resposta `404 Not Found`. A Django fornece algumas funções auxiliares para retornar isto assim podes não usar esta subclasse diretamente, mas é bom saber que está disponível.
+* `HttpResponseForbidden` - Este tipo de resposta pode ser usado quando não queres que o utilizador acesse uma parte da tua aplicação (por exemplo, estado de HTTP `403 Forbidden`).
-Aside from the subclasses, Django has other techniques to return `HttpResponse` instances without creating one yourself. The most common function is `render`.
+À parte das subclasses, a Django tem outras técnicas para retornar instâncias de `HttpResponse` sem criar um por ti mesmo. A função mais comum é `render`.
-`render` is a tool for working with templates. Templates are the topic
+`render` é uma ferramenta para trabalhar com modelos de marcação. Os modelos de marcação são o tópico
{{< web >}}
-of the next article,
+do próximo artigo,
{{< /web >}}
{{< book >}}
-of the next chapter,
+do próximo capítulo,
{{< /book >}}
-but here is a sneak peek.
+mas cá está um exemplar.
-You could write a view for a webpage and include a lot of HTML in your Python. HTML is the markup language of internet pages that we use to describe the format of a page.
+Tu poderias escrever uma visão para uma página e incluir muito código de HTML no teu programa de Python. A HTML é a linguagem de marcação das páginas da internet que usamos para descrever o formato ou estrutura duma página.
-This view might look like:
+Esta visão pode parecer-se com isto:
```python
from django.http import HttpResponse
@@ -169,13 +168,13 @@ def my_html_view(request):
return HttpResponse(response_content)
```
-While this works, it has many shortcomings:
+Embora isto funcione, tem muitos defeitos:
-1. The HTML chunk isn't reusable by other views. That doesn't matter much for this small example, but it would be a huge problem when you try to make many views that use a lot of markup and need to share a common look.
-2. The mixing of Python and HTML is going to get messy. Need proof? Go look at computing history and learn about {{< extlink "https://en.wikipedia.org/wiki/Common_Gateway_Interface" "CGI" >}}. It wasn't pretty.
-3. How can you join pieces of HTML together? Not easily.
+1. O pedaço de HTML não é reutilizável por outras visões. Isto não importa muito para este exemplo pequeno, mas seria um enorme problema quando tentares fazer mais visões que usam muita marcação e precisam partilhar um aspeto comum.
+2. A mistura de Python e HTML colocará as coisas em desordem. Precisa de prova? Olhe para história da computação e aprenda sobre a {{< extlink "https://en.wikipedia.org/wiki/Common_Gateway_Interface" "CGI" >}}. Ela não era bonita.
+3. Como podes juntar pedaços de HTML em grupo? Não claramente.
-With templates, we can separate the layout from the logic:
+Com modelos de marcação, podemos separar a disposição da lógica:
```python
# application/views.py
@@ -189,7 +188,7 @@ def my_html_view(request):
)
```
-And we would have another file named `template.html` containing:
+E teríamos um outro ficheiro nomeado `template.html` contendo:
```html
@@ -201,26 +200,26 @@ And we would have another file named `template.html` containing:
```
{{< web >}}
-The important part for this article is not about the templates themselves.
+A parte importante para este artigo não é sobre os próprios modelos de marcação.
{{< /web >}}
{{< book >}}
-The important part for this chapter is not about the templates themselves.
+A parte importante para este capítulo não é sobre os próprios modelos de marcação.
{{< /book >}}
-What's worth noting is that `render` loads the content from `template.html`, gets the output, and adds that output to an `HttpResponse` instance.
+O que é digno de menção é que `render` carrega o conteúdo de `template.html`, recebe a saída, e adiciona esta saída à uma instância de `HttpResponse`.
-That wraps up `HttpRequest` and `HttpResponse`. With those building blocks, we can now look at other ways that you can make Django views for your project.
+Isto envolve `HttpRequest` e `HttpResponse`. Com estes blocos de construção, podemos olhar em outras maneiras de puderes criar visões de Django para o teu projeto.
-## View Classes
+## Classes de Visão
-By now we've seen this relationship with views:
+Por agora vimos esta relação com as visões:
```text
HttpRequest -> view -> HttpResponse
```
-Views do not need to be functions exclusively. Django also provides tools to make views out of classes. These types of views derive from Django's `View` class.
+As visões não precisam de ser exclusivamente funções. A Django também fornece ferramentas para criar visões com classes. Estes tipos de visões derivam da classe `View` da Django.
-When you write a class-based view (often abbreviated to CBVs), you add instance methods that match up with HTTP methods. Let's see an example:
+Quando escreves uma visão baseada em classe (frequentemente abreviada para CBVs), adicionas métodos de instância que correspondem com os métodos de HTTP. Vamos ver um exemplo:
```python
# application/views.py
@@ -232,7 +231,7 @@ class SampleView(View):
return HttpResponse("Hello from a CBV!")
```
-The `get` method on the class corresponds to a `GET` HTTP request. `*args` and `**kwargs` are a common convention in Python to make a method or function that accepts any number of positional or keyword based arguments. We need these to match the expect method signature that Django requires for CBVs. Similarly, you would write a `post` method to respond to a `POST` HTTP request and so on. With that view defined, we can connect it to a URLconf:
+O método `get` na classe corresponde à uma requisição de HTTP `GET`. Os `*args` e `**kwargs` são uma convenção comum na Python para fazer um método ou função que aceite qualquer número de argumentos posicionais ou baseados em palavra-chave. Nós precisamos destes para corresponder a assinatura de método esperada que Django exige para visões baseadas em classe. De maneira semelhante, escreverias um método `post` para responder à uma requisição de HTTP `POST` e assim por diante. Com esta visão definida, podemos conectá-la à uma configuração de URL:
```python
# project/urls.py
@@ -245,30 +244,30 @@ urlpatterns = [
]
```
-Note that we don't pass `SampleView` to `path` as is. `path` expects a callable object, so we must call `as_view`, a class method that returns a function that will call the code in our class.
+Nota que não passamos `SampleView` para `path` como é. `path` espera um objeto chamável, assim devemos chamar `as_view`, um método de classe que retorna uma função que chamará o código da nossa classe.
-At this point, I would be suitably unimpressed if I were in your shoes. Why would we add all this boilerplate code when you can make a function and be done? If this were the full story, I would absolutely agree with you. A class-based view doesn't add much beyond the function-based version. If anything, CBVs have more to remember, so they are probably more confusing.
+Até este ponto, estaria como era de esperar pouco impressionado se estivesse no teu lugar. Porquê adicionaríamos todo este código cozinhado quando podes criar uma função e está feito? Se isto fosse a história completa, concordaria absolutamente contigo. Uma visão baseada em classe não adiciona muito além da versão baseada em função. Se nada, visões baseadas em classe têm mais para lembrar, assim são provavelmente mais confusas.
-Where class-based views begin to shine is when using some other classes beyond the initial `View` class.
+Onde as visões baseadas em classe começam a brilhar é quando usas algumas outras classes além da classe `View` inicial.
-Django includes a host of class-based views to use for a variety of purposes. We can explore a few of them with our limited exposure to the full framework so far.
+A Django inclui um hospedeiro de visões baseada em classe à usar para uma variedade de fins. Nós podemos explorar algumas delas com a nossa exposição limitada à abstração completa até aqui.
-## Out Of The Box Views
+## Visões Fora da Caixa
-I won't exhaustively cover all the class-based views because there are many. Also,
+Eu não cobrirei exaustivamente todas as visões baseadas em classe porque existem muitas. Além disto,
{{< web >}}
-if you're joining this article series from the beginning and have never done Django before,
+Se estiveres a juntar-se a esta séria de artigo desde o princípio e nunca criaste nada com a Django antes,
{{< /web >}}
{{< book >}}
-if you have never done Django before,
+Se nunca criaste nada com a Django antes,
{{< /book >}}
-then there will still be holes in your knowledge (which we will plug together!), and some of the views will not make much sense.
+então ainda existirá lacunas no teu conhecimento (que preencheremos juntos!), e algumas das visões não farão muito sentido.
### RedirectView
-Use `RedirectView` to send users of your site to a different place. You *could* make a view that returns an `HttpResponseRedirect` instance, but this class-based view can handle that for you.
+Use `RedirectView` para enviar os utilizadores da tua página para um lugar diferente. Tu *poderias* criar uma visão que retorna uma instância de `HttpResponseRedirect`, mas esta visão baseada em classe pode lidar com isto por ti:
-In fact, you can use `RedirectView` without subclassing it. Check this out:
+De fato, podes usar `RedirectView` sem criar uma subclasse dela. Observe:
```python
# project/urls.py
@@ -285,9 +284,9 @@ urlpatterns = [
]
```
-`RedirectView` can use `url` for a full URL, or it can use `pattern_name` if you need to route to a view that moved somewhere else in your project.
+`RedirectView` pode usar `url` para uma URL completa, ou pode usar `pattern_name` se precisares de enviar para uma visão que foi movida noutro lugar no teu projeto.
-`as_view` is what lets us avoid subclassing `RedirectView`. The arguments passed to `as_view` override any class attributes. The following two `RedirectView` uses are equivalent:
+`as_view` é o que permite-nos evitar criar um subclasse de `RedirectView`. Os argumentos passados para `as_view` sobrepõe quaisquer atributos da classe. Os dois usos de `RedirectView` seguintes são equivalentes:
```python
# project/urls.py
@@ -301,7 +300,7 @@ class SubclassedRedirectView(RedirectView):
urlpatterns = [
path("old-path/", SubclassedRedirectView.as_view()),
- # The RedirectView below acts like SubclassedRedirectView.
+ # O RedirectView abaixo comporta-se como SubclassedRedirectView.
path("old-path/", RedirectView.as_view(pattern_name='new-view')),
path("new-path/", NewView.as_view(), name='new-view'),
]
@@ -310,16 +309,16 @@ urlpatterns = [
### TemplateView
{{< web >}}
-Earlier in the article,
+Anteriormente neste artigo,
{{< /web >}}
{{< book >}}
-Earlier in the chapter,
+Anteriormente neste capitulo,
{{< /book >}}
-we briefly saw how to separate web page layout from the logic needed to build a page with templates.
+vimos brevemente como separar a disposição da página web da lógica precisou-se de construir uma página com modelos de marcação.
-Templates are so commonly used that Django provides a class that knows how to produce a response with nothing more than a template name.
+Os modelos de marcação são tão comummente usados que a Django fornece uma classe que sabe como produzir uma resposta com nada mais do que um nome de modelo de marcação.
-An example looks like:
+Um exemplo parece-se com:
```python
# application/views.py
@@ -329,45 +328,44 @@ class HomeView(TemplateView):
template_name = 'home.html'
```
-We will look at template views
-in greater detail
+Nós olharemos as visões de modelo de marcação em muitos mais detalhe
{{< web >}}
-in the next article
+no próximo artigo
{{< /web >}}
{{< book >}}
-in the next chapter
+no próximo capítulo
{{< /book >}}
-when we dive into templates.
+quando mergulharmos em modelos de marcação.
-### Other View Classes
+### Outras Classes de Visão
-Django's other class-based views serve a variety of purposes. Django has views that will:
+As outras visões baseadas em classe da Django servem a uma variedade de fins. A Django tem visões que:
-* Display and handle HTML forms so users can input data and send the data to the application.
-* Pull data from a database and show an individual record to the user (e.g., a webpage to see facts about a particular movie).
-* Pull data from a database and show information from a collection of records to the user (e.g., showing the cast of actors from a movie).
-* Show data from specific time ranges like days, weeks, and months.
+* Exibirão e manipularão formulários de HTML assim os utilizadores podem introduzir dados e enviar os dados para a aplicação.
+* Puxarão dados duma base de dados e mostrarão um registo particular ao utilizador (por exemplo, uma página para ver fatos sobre um filme específico).
+* Puxarão dados duma base de dados e mostrarão informação duma coleção de registos ao utilizador (por exemplo, mostrar o elenco de atores dum filme).
+* Mostrarão dados a partir limites de tempo específicos como dias, semanas, e meses.
-As we continue to explore Django, We will discuss these views when their related topic (like forms) is the primary subject
+Conforme continuamos a explorar a Django, discutiremos estas visões quando o seu tópico relacionado (como formulários) forem o assunto primário
{{< web >}}
-of an article.
+dum artigo.
{{< /web >}}
{{< book >}}
-of a chapter.
+dum capítulo.
{{< /book >}}
-For now, when you're developing your own views, try to remember that Django probably has a class-based view to aid your work.
+Por agora, quando estiveres a desenvolver as tuas próprias visões, tente lembrar que a Django provavelmente tem uma visão baseada em classe para apoiar o teu trabalho.
-## Useful View Decorators And Mixins
+## Misturas E Decoradores de Visão Úteis
-Before we finish the tour of views, let's discuss some useful decorators and mixin classes.
+Antes de terminarmos a excursão das visões, vamos discutir algumas classes de mistura e decoradores úteis.
-Decorators are a feature of Python (and many other languages) that let you extend a function with additional capabilities. A decorator can wrap a view function to provide new behavior to a view. Decorators are helpful when you have common functionality that you want to add to many views without copying and pasting a lot of code.
+Os decoradores são uma funcionalidade da Python (e muitas outras linguagens) que permitem-te estender uma função com capacidades adicionais. Um decorador pode envolver uma função de visão para fornecer um novo comportamento para uma visão. Os decoradores são úteis quando tens funcionalidade comum que queres adicionar para várias visões sem copiar e colar muito código.
-Mixin classes serve a very similar purpose as decorators, but use Python's multiple inheritance feature of classes to "mix in" the new behavior with an existing class-based view.
+As classes de mistura sem a um fim muito semelhante aos decoradores, mas usam a várias funcionalidades de herança de classes da Python para "misturar" o novo comportamento com uma visão baseada em classe existente.
-### Decorators To Know
+### Decoradores À Conhecer
-When you work with function-based views, there is a challenge when handling different HTTP methods. By default, a function based view can receive requests from *any* HTTP method. Some views will handle multiple methods like:
+Quando trabalhas com visões baseadas em função, existe um desafio quando lidas com diferentes métodos de HTTP. Por padrão, uma visão baseada em função pode receber requisições de *qualquer* método de HTTP. Algumas visões manipularão vários métodos como:
```python
# application/views.py
@@ -384,7 +382,7 @@ def multi_method_view(request):
return HttpResponseNotAllowed()
```
-This view uses the `request` instance `method` attribute to check the request's HTTP method. What if you only want your view to respond to one HTTP method? Let's say you only want to respond to a POST. We could write:
+Esta visão usa o atributo `method` da instância de `request` para verificar o método de HTTP da requisição. E se apenas quiseres que a tua visão responda a um método de HTTP? Vamos dizer que apenas queres responder à um `POST`. Nós poderíamos escrever:
```python
# application/views.py
@@ -399,7 +397,7 @@ def guard_clause_view(request):
return HttpResponse('Method was a POST.')
-# OR
+# OU
def if_clause_view(request):
if request.method == 'POST':
@@ -408,7 +406,7 @@ def if_clause_view(request):
return HttpResponseNotAllowed()
```
-Both techniques work, but the code is a little messier because of the extra indentation. Instead, we can use the `require_POST` decorator and let Django check the method for us:
+Ambas técnicas funcionam, mas o código está um pouco mais sujo por causa da indentação adicional. Ao invés disto, podes usar o decorador `require_POST` e deixar a Django verificar o método por nós:
```python
# application/views.py
@@ -420,9 +418,9 @@ def the_view(request):
return HttpResponse('Method was a POST.')
```
-This version states the expectation up front with the decorator and declares the contract that the view will work with. If a user tries a different method (like a `GET`), then Django will respond with HTTP status code `405`, which is an error code for "method not allowed."
+Esta versão estabelece a expetativa antecipadamente com o decorador e declara o contrato com o qual a visão trabalhará. Se um utilizador tentar um método diferente (como um `GET`), então a Django responderá com o código de estado de HTTP `405`, que é um código de erro para "method not allowed".
-Another common decorator you may encounter is the `login_required` decorator. When we get to the subject of user management, you'll see that we can make a protected view for an app by including this decorator:
+Um outro decorador com que podes encontrar é o decorador `login_required`. Quando chegarmos ao assunto de gestão de utilizador, verás que podemos criar uma visão protegida para uma aplicação incluindo este decorador:
```python
# application/views.py
@@ -434,9 +432,9 @@ def the_view(request):
return HttpResponse('This view is only viewable to authenticated users.')
```
-Any unauthenticated user will be redirected automatically to the login page for your web app.
+Qualquer utilizador não autorizado será redirecionado automaticamente para página de início de sessão para tua aplicação de Web.
-A final example of a useful built-in decorator is `user_passes_test`. This is another decorator used with the user management system that lets us control *which* users should be allowed to access a view. For instance, we could make a view that only staff-level users could access:
+Um exemplo final dum decorador embutido útil é `user_passes_test`. Este é um outro decorador usado com o sistema de gestão de utilizador que permite-nos controlar *quais* utilizadores deveriam ser permitidos acessar uma visão. Por exemplo, poderíamos criar uma visão que apenas utilizadores a nível do pessoal poderiam acessar:
```python
# application/views.py
@@ -448,9 +446,9 @@ def the_view(request):
return HttpResponse('Only visible to staff users.')
```
-The decorator takes a callable that will accept a single argument of a user object. The view will only be accessible if the return value of the test callable evaluates to `True`.
+O decorador recebe um chamável que aceitará um único argumento dum objeto do utilizador. A visão apenas é acessível se o valor de retorno do chamável de teste avalia para `True`.
-What I'm trying to show with these examples is how single decorators can quickly augment your views with new features. And, because of how decorators work to wrap functions, you can "stack" these together:
+O que estou a tentar mostrar com estes exemplos é como decoradores únicos podem rapidamente aumentar as tuas visões com novas funcionalidades. E, devido como os decoradores funcionam para envolver funções, podes "empilhar" estes um ao outro:
```python
# application/views.py
@@ -464,11 +462,11 @@ def the_view(request):
return HttpResponse('Only staff users may POST to this view.')
```
-### Mixins To Know
+### Misturas À Conhecer
-Mixin classes are to class-based views as decorators are to function-based views. This isn't *completely* true since class-based views can also use decorators, but it should give you an idea of where mixins fit.
+As classes de mistura são para visões baseadas em classe como os decoradores são para as visões baseadas em função. Isto não é *completamente* verdadeiro visto que as visões baseadas em classe também podem usar decoradores, mas isto deveria dar-te uma ideia de onde as misturas encaixam-se.
-Like the `login_required` and `user_passes_test` decorators, we have mixin equivalents of `LoginRequiredMixin` and `UserPassesTestMixin`. Maybe you have some template views that should only be accessible to authenticated users or staff-level users. Those views could look like:
+Tais como os decoradores `login_required` e `user_passes_test`, temos equivalentes de mistura de `LoginRequiredMixin` e `UserPassesTestMixin`. Talvez tenhas algumas visões de modelo de marcação que apenas deveriam ser acessíveis aos utilizadores autenticados ou utilizadores a nível do pessoal. Estas visões poderiam parecer-se com:
```python
# application/views.py
@@ -485,38 +483,38 @@ class StaffProtectedView(UserPassesTestMixin, TemplateView):
return self.request.user.is_staff
```
-You can see that these views are similar to their decorator counterparts with a slightly different usage pattern.
+Tu podes ver que estas visões são semelhantes aos equivalente de decorador com um padrão de uso ligeiramente diferente.
-One thing worth noting with mixins is their placement. Because of the way that Python handles multiple inheritance, you should be sure to include mixin classes to the left in the list of inherited base classes. This will ensure that Python will behave appropriately with these classes. The exact reason for this placement is because of Python's method resolution order (MRO) rules when using multiple inheritance. MRO is outside of our scope, but that's what you can search for if you want to learn more.
+Uma coisa digna de atenção com as misturas são as suas colocações. Por causa da maneira que a Python manipula várias heranças, deves estar certo de incluir classes de mistura à esquerda na lista de classes de base herdadas. Isto garantirá que a Python comportar-se-á apropriadamente com estas classes. A razão exata para esta colocação é por causa das regras de ordem de resolução de método da Python quando usas várias heranças. A ordem de resolução de método está fora do âmbito, mas é isto que podes procurar se quiseres saber mais.
-There are plenty of other mixin classes. Most of Django's built-in class-based views are constructed by composing various mixin classes together. If you'd like to see how they are constructed, check out {{< extlink "https://ccbv.co.uk/" "Classy Class-Based Views" >}}, a site showing the built-in CBVs and the mixins and attributes available to those classes.
+Existem muitas outras classes de mistura. A maioria das visões baseadas em classe embutida da Django são construídas compondo várias classes de mistura juntas. Se gostarias de ver como são construídas, consulte {{< extlink "https://ccbv.co.uk/" "Visões Baseadas em Classe Requintadas" >}}, um local mostrando as visões baseadas em classe embutidas e misturas e atributos disponíveis para estas classes.
-## Summary
+## Sumário
-That's a wrap on view fundamentals. We've looked at:
+Isto resume os fundamentos da visão. Nós vimos:
-* View functions
-* `HttpRequest` and `HttpResponse`
-* View classes
-* Some built-in supporting views
-* Decorators and mixins that supercharge views.
+* Funções de visão
+* `HttpRequest` e `HttpResponse`
+* Classes de visão
+* Algumas visões de suporte embutidas
+* Decoradores e misturas que sobrecarregam as visões.
{{< web >}}
-In the next article,
+No próximo artigo,
{{< /web >}}
{{< book >}}
-In the next chapter,
+No próximo capítulo,
{{< /book >}}
-we'll see how views can mix static layout with the dynamic data we provide by using templates. Templates are the workhorse for your Django-based user interfaces. We're going to see:
+veremos como as visões podem misturar disposição estática com os dados dinâmicos que fornecemos usando modelos de marcação. Os modelos de marcação são os animais de carga para as tuas interfaces de utilizador baseadas na Django. Nós veremos:
-* How to set up templates for your site
-* Ways to call templates from views
-* How to use data
-* How to handle logic
-* Built-in functions available to templates
-* Customizing templates with your own code extensions
+* Como configurar os modelos de marcação para a tua aplicação
+* Maneiras de chamar os modelos de marcação a partir das visões
+* Como usar dados
+* Como manipular a lógica
+* Funções embutidas disponíveis para os modelos de marcação
+* Personalização de modelos de marcação com as tuas próprias extensões de código
{{< web >}}
-If you'd like to follow along with the series, please feel free to sign up for my newsletter where I announce all of my new content. If you have other questions, you can reach me online on Twitter where I am {{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
+Se gostarias de seguir com a série, sinta-se livre para inscrever-se no meu boletim informativo onde anúncio todos os meus novos conteúdos. Se tiveres outras questões, podes contactar-me na Twitter onde sou {{< extlink "https://twitter.com/mblayman" "@mblayman" >}}.
{{< /web >}}
From e80041322d91b8d361b0648fabaf190f75ecad4b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nazar=C3=A9=20da=20Piedade?=
Date: Wed, 12 Jul 2023 15:48:18 +0100
Subject: [PATCH 24/30] docs(pt): translate
`understand-django/templates-user-interfaces.pt.md`
---
...2020-04-02-templates-user-interfaces.pt.md | 288 +++++++++---------
1 file changed, 144 insertions(+), 144 deletions(-)
diff --git a/content/understand-django/2020-04-02-templates-user-interfaces.pt.md b/content/understand-django/2020-04-02-templates-user-interfaces.pt.md
index 19fff2f8..529d4bf2 100644
--- a/content/understand-django/2020-04-02-templates-user-interfaces.pt.md
+++ b/content/understand-django/2020-04-02-templates-user-interfaces.pt.md
@@ -1,7 +1,7 @@
---
-title: "Templates For User Interfaces"
+title: "Modelos de Marcação para as Interfaces de Utilizador"
description: >-
- When your Django application sends back a response with your user interface, templates are the tool you'll use to produce that user interface. This article looks at what templates are and how to use them.
+ Quando a tua aplicação de Django devolve uma resposta com a tua interface de utilizador, os modelos de marcação são a ferramenta que usarás para produzir esta interface de utilizador. Este artigo olha considera o que os modelos de marcação são e como usá-los.
image: img/django.png
type: post
categories:
@@ -15,17 +15,17 @@ tags:
---
{{< web >}}
-In the previous [Understand Django]({{< ref "/understand-django/_index.pt.md" >}}) article, we looked at the fundamentals of using views in Django. This article will focus on templates.
+No artigo anterior da [Entendendo a Django]({{< ref "/understand-django/_index.pt.md" >}}), vimos os fundamentos de usar visões na Django. Este artigo focar-se-á em modelos de marcação.
{{< /web >}}
-Templates are your primary tool in a Django project for generating a user interface. With templates, you'll be able to build the pages that users will see when they visit your web app. Let's see how templates hook into views and what features Django provides with its template system.
+Os modelos de marcação são a tua principal ferramenta num projeto de Django para gerar uma interface de utilizador. Com os modelos de marcação, seremos capazes de construir as páginas que os utilizadores verão quando visitarem a tua aplicação de Web. Vamos ver como os modelos de marcação ligam-se as visões e quais funcionalidades a Django fornece com o seu sistema de modelo de marcação.
{{< understand-django-series-pt "templates" >}}
-## Set Up Templates
+## Configure os Modelos de Marcação
-We need a place for templates to live. Templates are static files that Django will fill in with data. In order to use those files, we must instruct Django on where to find them.
+Nós precisamos dum lugar para os modelos de marcação morarem. Os modelos de marcação são ficheiros estáticos que a Django preencherá com dados. Para usar estes ficheiros, devemos instruir a Django sobre onde encontrá-los.
-Like most parts of Django, this configuration is in your project's settings file. After you use `startproject`, you can find a section in your settings file that will be called `TEMPLATES`. The section should look something like:
+Tal como a maioria das partes da Django, esta configuração está no ficheiro de definições do teu projeto. Depois de usares `startproject`, podes encontrar uma seção no teu ficheiro de definições que será chamada de `TEMPLATES`. A seção deve parecer-se com algo como:
```python
# project/settings.py
@@ -45,13 +45,13 @@ TEMPLATES = [{
}]
```
-Django's template system can use multiple template backends. The backends dictate how your templates will work. I would recommend sticking with the default Django template language. This language has the tightest integration with the framework and the strongest support.
+O sistema de modelo de marcação da Django pode usar vários backend de modelo de marcação. Estes ditam como os teus modelos de marcação funcionarão. Eu recomendaria aguentar com a linguagem de modelo de marcação da Django padrão. Esta linguagem tem a mais firme integração com a abstração e o mais forte suporte.
-The next thing to notice is `APP_DIRS` with its value of `True`. For the Django template language, setting this value to `True` will cause Django to look for template files within a `templates` directory in each Django application in your project. Note that this also includes any third party applications so you should probably leave this set to `True`.
+A próxima coisa a notar é `APP_DIRS` com o seu valor de `True`. Para a linguagem de modelo de marcação da Django, definir este valor para `True` causará a Django à procurar pelos ficheiros de modelo de marcação dentro dum diretório `templates` em cada aplicação de Django no teu projeto. Nota que isto também inclui quaisquer aplicações de terceiros então deverias provavelmente deixar esta definida para `True`.
-So, where should *your* templates go? There are different schools of thought in the Django community. Some developers believe in having all templates within applications. Others ascribe to having all your project's templates in a single directory. I'm in this second category of developers. I find it valuable to keep all of the templates for my entire project within a single directory.
+Então, onde deveriam os *teus* modelos de marcação ir? Existem diferentes escolas de pensamento na comunidade da Django. Alguns programadores acreditam que todos os modelos de marcação deveriam estar dentro das aplicações. Outros imputam para que todos os modelos de marcação do teu projeto deveriam estar num único diretório. Eu estou nesta segunda categoria de programadores. Eu considero valioso manter todos os modelos de marcação para o meu projeto inteiro dentro dum único diretório.
-From my perspective, keeping templates in a single directory makes it very clear where all the layout and UI in your system will live. To use that pattern, we must set the `DIRS` variable with the directory that we want Django to include. I recommend keeping a `templates` directory at the root of your project. If you do that, your `DIRS` value will change to something like:
+Da minha perspetiva, manter os modelos de marcação num único diretório torna mais claro onde toda disposição e interface de utilizador no teu sistema morarão. Para usar este padrão, devemos definir a variável `DIRS` com o diretório que queremos que a Django inclua. Eu recomendo manter um diretório `templates` na raiz do teu projeto. Se fizeres isto, o valor do tua `DIRS` mudará para algo como:
```python
# project/settings.py
@@ -63,21 +63,21 @@ TEMPLATES = [
]
```
-Finally, there is `OPTIONS`. Each backend can accept a variety of options. `startproject` sets a number of context processors. We'll come back to context processors later
+Finalmente, existe `OPTIONS`. Cada backend pode aceitar uma variedade de opções. `startproject` define um número de processadores de contexto. Voltaremos para os processadores de contexto depois
{{< web >}}
-in this article.
+neste artigo.
{{< /web >}}
{{< book >}}
-in this chapter.
+neste capítulo.
{{< /book >}}
-With your templates set up, you're ready to go!
+Com os teus modelos de marcação definidos, estás pronto para avançares!
-## Using Templates With Render
+## Usando os Modelos de Marcação com a `render`
-Django builds your user interface by *rendering* a template. The idea behind rendering is that dynamic data is combined with a static template file to produce a final output.
+A Django construi a tua interface de utilizador *interpretando* um modelo de marcação. A ideia por trás da interpretação é que os dados dinâmicos são combinados com um ficheiro de modelo de marcação estático para produzir uma saída final.
-To produce an `HttpResponse` that contains rendered output, we use the `render` function. Let's see an example in the form of a function-based view (FBV):
+Para produzir um `HttpResponse` que contém a saída interpretada, usamos a função `render`. Vamos ver um exemplo na forma duma visão baseada em função (FBV):
```python
# application/views.py
@@ -93,39 +93,39 @@ def hello_view(request):
)
```
-In this example, the view would use a template located in `templates/hello.txt` which could contain:
+Neste exemplo, a visão usaria um modelo de marcação localizado em `templates/hello.txt` que poderia conter:
```txt
Hello {{ name }}
```
-When this view responds to a request, a user would see "Hello Johnny" in their browser. There are some interesting things to note about this example.
+Quando esta visão responde à uma requisição, um utilizador veria "Hello Johnny" no seu navegador. Existem algumas coisas interessantes à notar sobre este exemplo.
-1. The template can be any plain text file type. Most often we will use HTML to make a user interface so you will often see `some_template.html`, but the Django template system can render on any type.
-2. In the process of rendering, Django took the context data dictionary and used its keys as variable names in the template. Because of special double curly brace syntax, the template backend swapped out `{{ name }}` for the literal value of "Johnny" that was in the context.
+1. O modelo de marcação pode ser qualquer tipo de ficheiro de texto. Mais frequentemente usaremos HTML para fazer uma interface de utilizador então frequentemente veremos `some_template.html`, mas o sistema de modelo de marcação da Django pode interpretar qualquer tipo.
+2. No processo de interpretação, a Django pegou o dicionário de dados do contexto e usou suas chaves como nomes de variáveis no modelo de marcação. Por causa da sintaxe especial de chavetas duplas, o backend do modelo de marcação trocou `{{ name }}` pelo valor literal "Johnny" que estava no contexto.
-This idea of mixing context and static layout is the core concept of working with templates.
+Esta ideia de misturar contexto e disposição estática é o conceito fundamental do trabalho com modelos de marcação.
{{< web >}}
-The rest of this article builds
+O resto deste artigo baseasse
{{< /web >}}
{{< book >}}
-The rest of this chapter builds
+O resto deste capítulo baseasse
{{< /book >}}
-on this root concept and shows what else is possible in the Django template language.
+neste conceito de origem e mostra o que mais é possível na linguagem do modelo de marcação da Django.
-As an aside, HTML is a topic that we are not going to explore directly. HTML, the Hypertext Markup Language, is the language used on the web to describe the structure of a page. HTML is composed of tags and many of these tags work in pairs. For example, to make a *paragraph*, you can use a `p` tag, which is represented by wrapping `p` with greater than and less than symbols to form the "opening" tag. The "closing" tag is similar, but it includes a forward slash.
+Como um aparte, HTML é um tópico que não iremos explorar diretamente. HTML, **Hypertext Markup Language**, Linguagem de Marcação de Hipertexto, é a linguagem usada na Web para descrever a estrutura duma página. HTML é composta de marcadores e muitos destes marcadores trabalham em pares. Por exemplo, para fazer um *parágrafo*, podes usar o marcador `p`, que é representado envolvendo `p` com os sinais maior do que e menor do que para formar o marcador de "abertura". O marcador de "encerramento" é semelhante, mas este inclui uma barra oblíqua:
```html
This is a paragraph example.
```
{{< web >}}
-From the last article,
+Do último artigo,
{{< /web >}}
{{< book >}}
-From the last chapter,
+Do último capítulo,
{{< /book >}}
-you may recall seeing the `TemplateView`. In those examples, we provided a template name, and I declared that Django would take care of the rest. Now you can start to understand that Django takes the template name and calls code similar to `render` to provide an `HttpResponse`. Those examples were missing context data to combine with the template. A fuller example replicating the `hello_view` function-based view as a class-based-view would look like:
+podes lembrar-te de ver a `TemplateView`. Naqueles exemplos, fornecemos um nome de modelo de marcação, e declarei que a Django cuidaria do resto. Agora podes começar a entender que a Django recebe o nome do modelo de marcação e chama um código semelhante ao `render` para fornecer um `HttpResponse`. Aqueles modelos de marcação estavam com o dado de contexto em falta para combinar com o modelo de marcação. Um exemplo mais completo replicando a visão baseada em função `hello_view` como uma visão baseada em classe parecer-se-ia com:
```python
# application/views.py
@@ -146,19 +146,19 @@ class HelloView(TemplateView):
return context
```
-This example uses `get_context_data` so that we can insert our "dynamic" data into the rendering system to give us the response we want.
+Este exemplo usa `get_context_data` para que possamos inserir os nossos dados "dinâmicos" no sistema de interpretação para dar-nos a resposta que queremos.
-In a real application, a lot of the code that we need to write focuses on building up a truly dynamic context. I'm using static data in these examples to keep the mechanics of the template system clear. When you see me use `context`, try to imagine more complex data building to create a user interface.
+Numa aplicação real, muito do código que precisamos de escrever concentra-se em construir um contexto verdadeiramente dinâmico. Eu estou a usar dados estáticos nestes exemplos para manter as mecânicas do sistema de modelo de marcação claras. Quando veres-me a usar `context`, tente imaginar a construção de dados mais complexos para criar uma interface de utilizador.
-Those are the fundamentals of rendering. We'll now turn our attention to what the Django template language is capable of.
+Estes são os fundamentos da interpretação. Agora voltaremos a nossa atenção para o que a linguagem de modelo de marcação é capaz de fazer.
-## Templates In Action
+## Modelos de Marcação em Ação
-When using templates, we take context data and insert it into the placeholders within the template.
+Quando usamos os modelos de marcação, recebemos o dado de contexto e o inserimos em espaços reservados dentro do modelo de marcação.
-Template variables are the most basic form of filling placeholders with context. The previous section showed an example by using the `name` variable. The context dictionary contains a `name` key, whose value appears anywhere in the template where that key is surrounded by double curly braces.
+As variáveis do modelo de marcação são a forma mais básica de preencher os espaços reservados com contexto. A seção anterior mostrava um exemplo usando a variável `name`. O dicionário de contexto contém um chave, cujo valor aparece em qualquer parte no modelo de marcação onde esta chave é envolvida por duplas chavetas.
-We can also use a dot access when the context data is more complex. Let's say your template gets context like:
+Nós podemos também usar um ponto de acesso quando o dado de contexto for mais complexo. Vamos dizer que o teu modelo de marcação recebe um contexto como:
```python
context = {
@@ -171,7 +171,7 @@ context = {
}
```
-Your Django template *won't* work if you try to access this context data like a regular dictionary (e.g., `{{ address['street'] }}`). Instead, you would use dot notation to get to the data in the dictionary:
+O teu modelo de marcação da Django *não funcionará* se tentares acessar este dado de contexto como um dicionário normal (por exemplo, `{{ address['street'] }}`). Ao invés disto, usarias a notação de ponto para teres acesso aos dados no dicionário:
```txt
The address is:
@@ -179,7 +179,7 @@ The address is:
{{ address.city }}, {{ address.state }} {{ address.zip_code}}
```
-This would render as:
+Isto interpretaria como:
```txt
The address is:
@@ -187,11 +187,11 @@ The address is:
Beverly Hills, CA 90210
```
-Django templates also try to be flexible with the types of context data. You could also pass in a Python class instance like an `Address` class with attributes that are the same as the keys in our previous dictionary. The template would work the same.
+Os modelos de marcação da Django também tentam ser flexíveis com os tipos de dados de contexto. Tu poderias também passar uma instância de classe da Python como uma classe `Address` com atributos que são iguais as chaves nos dicionário anterior. O modelo de marcação funcionaria da mesma maneira.
-The core template language also includes some standard programming logic keywords by using *tags*. Template tags look like `{% some_tag %}` whereas template variables look like `{{ some_variable }}`. Variables are meant to be placeholders to fill in, but tags offer more power.
+A linguagem de modelo de marcação fundamental também inclui algumas palavras-chave de lógica de programação padrão usando *marcadores*. Os marcadores do modelo de marcação parecem-se com `{% some_tag %}` ao passo que as variáveis do modelo de marcação parecem-se com `{{ some_variable }}`. As variáveis estão destinadas a serem espaço reservados à preencher, mas os marcadores oferecem mais poder.
-We can start with two core tags, `if` and `for`. The `if` tag is for handling conditional logic that your template might need:
+Nós podemos começar com dois marcadores fundamentais, `if` e `for`. O marcador `if` é para lidar com lógica condicional que o teu modelo de marcação pudesse precisar:
{{< web >}}
```django
@@ -208,7 +208,7 @@ We can start with two core tags, `if` and `for`. The `if` tag is for handling co
```
{{< /book >}}
-This example will only include this welcome message HTML header tag when the user is logged in to the application. We started the example with an `if` tag. Observe that the `if` tag requires a closing `endif` tag. Templates must respect whitespace since your layout might depend on that whitespace. The template language can't use whitespace to indicate scope like it can with Python so it uses closing tags instead. As you might guess, there are also `else` and `elif` tags that are accepted inside of an `if`/`endif` pair:
+Este exemplo apenas incluirá este marcador de cabeçalho de texto de HTML da mensagem de boas-vindas quando o utilizador estiver com a sua sessão iniciada nesta aplicação. Nós começamos o exemplo com um marcador `if`. Observe que o marcador `if` exige um marcador `endif` de encerramento. Os modelos de marcação devem respeitar o espaço em branco visto que a tua disposição pode depender deste espaço em branco. A linguagem de modelo de marcação não pode usar espaço em branco para indicar âmbito como pode com a Python então ao invés disto usa marcadores de encerramento. Como podes supor, também existem os marcadores `else` e `elif` que são aceites dentro um par `if`/`endif`:
{{< web >}}
```django
@@ -229,9 +229,9 @@ This example will only include this welcome message HTML header tag when the use
```
{{< /book >}}
-In this case, only one of the header tags will render depending on whether the user is authenticated or not.
+Neste caso, apenas um dos marcadores de cabeçalho de texto interpretará dependendo de se o utilizador estiver autenticado ou não.
-The other core tag to consider is the `for` loop tag. A `for` loop in Django templates behaves as you might expect:
+O outro marcador fundamental a considerar é o marcador de laço de repetição `for`. Um lado de repetição `for` nos modelos de marcação da Django comportam-se como podes esperar:
{{< web >}}
```django
@@ -254,7 +254,7 @@ The other core tag to consider is the `for` loop tag. A `for` loop in Django tem
```
{{< /book >}}
-Django will loop over iterables like lists and let users output template responses for each entry in an iterable. If the example above had a list of `items` in the context like:
+A Django iterará sobre os iteráveis como listas e deixarão os utilizadores produzirem respostas de modelo de marcação para cada entrada num iterável. Se o exemplo acima tivesse uma lista de `items` no contexto como:
```python
items = [
@@ -263,7 +263,7 @@ items = [
]
```
-Then the output would look roughly like:
+Então a saída parecer-se-ia aproximadamente com:
```html
Prices:
@@ -273,7 +273,7 @@ Then the output would look roughly like:
```
-Occasionally, you may want to take some specific action on a particular element in the `for` loop. Python's built in `enumerate` function isn't available directly in templates, but a special variable called `forloop` is available inside of a `for` tag. This `forloop` variable has some attributes like `first` and `last` that you can use to make templates behave differently on certain loop iterations:
+Ocasionalmente, podes querer tomar alguma específica sobre um elemento particular no laço de repetição `for`. A função `enumerate` embutida da Python não está disponível diretamente nos modelos de marcação, mas uma variável especial chamada `forloop` está disponível dentro dum marcador `for`. Esta variável `forloop` tem alguns atributos como `first` e `last` que podes usar para fazer os modelos de marcação comportarem-se de maneira diferente em certas iterações do laço de repetição:
{{< web >}}
```django
@@ -292,7 +292,7 @@ Counting:
```
{{< /book >}}
-This example would produce:
+Este exemplo produziria:
```txt
Counting:
@@ -301,13 +301,13 @@ Counting:
3 is last!
```
-Equipped with variables, `if` tags, and `for` tags, you should now have the ability to make some fairly powerful templates, but there's more!
+Equipado com variáveis, marcadores `if`, e marcadores `for`, agora deverias ter a habilidade de criar alguns modelos de marcação razoavelmente poderosos, mas existem mais!
-### More Context On Context
+### Mais Contexto sobre Contexto
-In the setup of the templates settings, we glossed over context processors. Context processors are a valuable way to extend the context that is available to your templates when they are rendered.
+Na configuração das definições de modelos de marcação, não falámos dos processadores de contexto. Os processadores de contexto são uma maneira valiosa de estender o contexto que está disponível para os teus modelos de marcação quando forem interpretados.
-Here's the set of context processors that Django's `startproject` command brings in by default:
+Neste exemplo temos um conjunto de processadores de contexto que o comando `startproject` da Django trás por padrão:
```python
'context_processors': [
@@ -318,20 +318,20 @@ Here's the set of context processors that Django's `startproject` command brings
],
```
-Context processors are functions (technically, callables, but let's focus on functions) that receive an `HttpRequest` and must return a dictionary. The returned dictionary merges with any other context that will be passed to your template.
+Os processadores de contexto são (tecnicamente, chamáveis, mas vamos nos concentrar nas funções) que recebem um `HttpRequest` e deve retornar um dicionário. O dicionário retornado funde-se com qualquer outro contexto que será passado para o modelo de marcação.
-Conceptually, when preparing to render and given a `context` dictionary that was passed to `render`, the template system will do something like:
+Concetualmente, quando preparas para interpretar e dado um dicionário de `context` que era passado para `render`, o sistema de modelo de marcação farão algo como:
```python
for processor in context_processors:
context.update(processor(request))
-# Continue on to template rendering
+# Continue na interpretação de modelo de marcação
```
-The actual code in the template system is more complex than this concept code sketch, but not by much!
+O verdadeiro código no sistema de modelo de marcação é mais complexo do que este esboço de código de conceito, mas não muito.
-We can look at the actual definition of the `request` context processor included in that default list:
+Nós podemos olhar na verdadeira definição do processador de contexto `request` incluído naquela lista padrão:
```python
# django/template/context_processors.py
@@ -340,27 +340,27 @@ def request(request):
return {'request': request}
```
-That's it! Because of this context processor, the `request` object will be available as a variable to any template in your project. That's super powerful.
+Já está! Por causa deste processador de contexto, o objeto `request` estará disponível como variável em qualquer modelo de marcação no teu projeto. Isto é superpoderoso.
-The "dark side" of context processors is that they run for all requests. If you write a context processor that is slow and does a lot of computation, *every request* will suffer that performance impact. So use context processors carefully.
+O "lado escuro" dos processadores de texto é que executam para todas as requisições. Se escreveres um processador de contexto que é lento e que faz muito cálculo, *cada requisição* sofrerá este impacto de desempenho. Então use os processadores de contexto cuidadosamente.
-### Reusable Chunks Of Templates
+### Pedaços Reutilizáveis de Modelos de Marcação
-Now let's talk about one of the powerhouse features of the template system: reusable pieces.
+Agora falaremos sobre uma das funcionalidades do núcleo de atividade do sistema de modelo de marcação: pedaços reutilizáveis.
-Think about a website. Most pages have a similar look and feel. They do this by repeating a lot of the same HTML, which is Hypertext Markup Language that defines the structure of a page. These pages also use the same CSS, Cascading Style Sheets, which define the styles that shape the look of the page elements.
+Pense sobre um local da Web. A maioria das páginas têm uma aparência semelhante. Elas fazem isto repetindo muito do mesmo HTML, que é a Linguagem de Marcação de Hipertexto que define a estrutura duma página. Estas páginas também usam a mesma CSS, Folhas de Estilo em Cascata, que define os estilos que moldam a aparência dos elementos da página.
-Imagine you're asked to manage a site and you need to create two separate pages. The homepage looks like:
+Imagine que és convidado para gerir uma aplicação e precisas de criar duas páginas separadas. A página principal parece-se com:
```html
@@ -374,7 +374,7 @@ Imagine you're asked to manage a site and you need to create two separate pages.
```
-And here is a page to learn about the company behind the website:
+E cá está uma página para conhecer a empresa por trás do local da Web:
```html
@@ -388,9 +388,9 @@ And here is a page to learn about the company behind the website: