Creating Interactive Views in Django
Tags: django, python, software, webdev
Published: 2020-04-24 12:54:55

---
title: Creating Interactive Views in Django
published: true
date: 2020-04-23 16:21:00 UTC
tags: Django,Python,Software,WebDevelopment
series: Getting Into Django
canonical_url: https://hackersandslackers.com/creating-django-views/
---

![Creating Interactive Views in Django](https://hackersandslackers-cdn.storage.googleapis.com/2020/04/django-views-1.jpg)

Aside from walking through painful "hello world" tutorials or setting project configurations, there isn't anything more fundamental about web frameworks than creating views. We've already tackled the uninspiring ordeal of setting up a Django project in our [last post](https://hackersandslackers.app/getting-started-django/), which allows us to turn our full attention to the most fundamental component of web frameworks.

Earlier MVC frameworks (like Django) championed the enforcement of **separation of concerns** in mainstream web development. The concept of encapsulating logic into single-purpose modules was hardly a new one in computer science at the time. Still, most employed web developers weren't exactly computer scientists in the early 2000s. Unless you happened to be building impactful software, most companies were fine doing horrible things with PHP, Drupal, Wordpress, or whichever awful LAMP stack variation happened to be your thing at the time.

## What We Need to Know About Django Views

Whether you're a veteran of MVC frameworks or a Django newcomer, the concept of **views** in a web framework seems straightforward. In essence, **views** are chunks of logic that accept incoming requests to a URL in your app and outputs the proper response. We begin by dissecting **views** into their most straightforward pattern:

1. Parse an HTTP request form a user attempting to reach a page.
2. Output the response based on the request.

Normally that'd be the crux of it, but Django isn't your "average" framework... it's _The Grandaddy of all Frameworks_. Django has been around long enough to have seen or dealt with every software design challenge as they pertain to web frameworks. Avoiding tedious patterns is undoubtedly desirable, but this may come at the cost of complexity to new users. See, Django actually has three "types" of views:

- **Function-based views**: Defining views as Python functions is standard practice for most developers, as it's generally quicker to write _simplistic_ views as functions. There's nothing class-based views can achieve that Function-based views cannot; the difference comes down to shortcuts/mixins available to class-based views.
- **Class-based views**: While they may appear bulkier than their function-based counterparts at first glance, class-based views typically save time over the long run for several reasons. The most advantages of class-based Django views are their ability to dodge repetitive boilerplate code via the use of Mixins, which automatically handle common yet time-consuming view patterns, such as views that handle forms, redirects, etc. Just like regular Python classes, class-based views can also extend one another to share common logic between views.
- **Model-driven class-based views**: I'm not sure if there's an official name for these, but "model-driven class-based views" are class-based views that specifically deal with models. These are views that are coupled with data models to produce views that list or mutate records associated with Django data models.

### What We'll Cover

This post is focused explicitly on **function-based views**. As we break down the anatomy of Django views, this tutorial should equip you to write Django views that handle a relatively impressive amount of user interaction. The views we'll build today will be capable of:

- Fetching and utilizing user metadata.
- Handling form validation and submission.
- Creating, modifying, and fetching records from a database.
- Serving user profiles with dynamic URLs.

The full source code for this tutorial is hosted in an accompanying [Github repository](https://github.com/hackersandslackers/django-views-tutorial). I've also deployed this as a working demo to showcase the functionality covered in this post:

https://github.com/hackersandslackers/django-views-tutorial

**NOTE**: If you're new to Django, I'd strongly recommend starting with [the previous post in this series](https://hackersandslackers.com/getting-started-django/), which details getting set up with a database and so forth. Don't try to skip steps, wise guy.

## Putting the "V" in MVC

You probably already know MVC stands for **Model**, **View**, **Controller** (I know, I know... bear with me on this). The reason I rehash this concept is to emphasize what views **_are_** and what they **_are not_**. Roll your eyes if you want, but we both know about the 800-line view functions you've created, and might even still be creating. So yeah, this applies to you:

![Creating Interactive Views in Django](https://hackersandslackers-cdn.storage.googleapis.com/2020/04/MVC.jpg)<figcaption>MVC (or "MTV") in Django</figcaption>

Django lacks terminology for "controllers," hence its self-proclaimed status as **Model, Template, View** framework. Aside from the unfortunate acronym, the underlying concepts of MVC remain the same: **Templates** are user-facing interfaces, **Views** are business logic which parses contextual input to serve templates, and **Models** are abstractions of data in our database.

In our example above, both of our views accomplish the same underlying goal: they take an input and produce an output. It isn't important to focus on whether the "input" coming is from the client-side or triggered by a backend event. The point is that view logic should strive to be as straightforward as a transaction via a Burger King drive-through. Views logic isn't the place for complicated data analysis or time-consuming batch services (that's what message queues are for). Your view's focus should be getting the user what they want, or at least some feedback, as quickly as possible.

## Deconstructing a View

Whenever you visit a page on the internet, your browser sends a **request** which contains things like _headers_ (metadata about who you are), _parameters_ (variables), HTTP method, and occasionally a body of data. Upon doing so, you expect to receive a **response** in return, which consists of mostly the same pieces. Depending on the nature of the request you make (i.e., whether or not you're logged in), you should expect a different response to come back to you.

Given that a view's only purpose is to handle user requests, your views should be designed to do nothing more than parse **requests** to serve a proper **response**.

Django views share the _same terminology_ as the things they're abstracting. Take requests, for instance: when your webserver (like Nginx or whatever) receives a request, the request information is forwarded to Django in a neat `request` object. This object contains everything you'd expect. Here are just a few examples:

- **Headers**: Every incoming `request` contains headers sent by the client's browser to Django as part of the request. `request.headers.items()` returns a list of tuples per header, where the first value is the _header name (like_ **Content-Type** ), and the second is the _header value_ (such as **text/plain** ).
- **Cookies**: `request.COOKIES` contains cookie and session identifier information that is unique to the user making the request. This gets returned as a dictionary of values, such as a _session_ identifier, and the user's _csrftoken._
- **Path**: `request.path` contains the filepath of the page the user is attempting to hit (in our case, this would be _/simple\_views/get\_json_).
- **Method**: The HTTP method of the incoming request (GET, POST, etc.) can be determined with `request.method`. This is useful for views that accept multiple HTTP methods and contain logic unique to dealing with each case.
- **Body** : `request.body` contains data passed along in the body of a POST request (if applicable).
- **Scheme** : `request.scheme`: Whether the user attempted an _HTTP_ or _HTTPS_ request.

On the other hand, responses are handled in Django via a built-in function called `HttpResponse()`. Views return responses to users expecting to pass along three parts:

- **Content**: Such as a page template or JSON response
- **Status**: HTTP response code (200, 404, etc)
- **Content-type**: The type of content being returned.

The most important thing to acknowledge about the above three parts is the _absence of anything else_. Within the confines of the above, the most complicated request/response scenario might involve parsing some user input, manipulating some data, and providing a response summarizing the action of what happened. This is what we should strive to accomplish in our views: getting responses out for requests coming in. Our views should be the web equivalent of shitty fast-food drive-thru windows.

## View #1: JSON Response

Views don't get more simple than serving some simple JSON. Here's the most simple conceivable view we can create:

```python
from django.http import JsonResponse


def get_json_view(request):
    """Return request metadata to the user."""
    data = {'url': request.path}
    return JsonResponse(data)
```
<figcaption>views.py</figcaption>

Views that return `JsonResponse` will convert a Python dictionary into JSON object and serve the resulting JSON to the user. When we visit the URL associated with this view, we'll receive a JSON object which tells us the path of the view we're looking at, courtesy of `request.path`.

```json
{
  "path": "/function_views/get_json"
}
```
<figcaption>What a user sees after hitting <code>get_json_view()</code>.</figcaption>

There's a lot more we can find out about a user from Django's `request` object besides the path they're hitting. Let's beef up our view by checking out the **headers** and **cookies** coming from the requesting user:

```python
from django.http import JsonResponse


def get_json_view(request):
    """Return request metadata to the user."""
    data = {'received_headers': dict(request.headers.items()),
            'client_cookies': request.COOKIES,
            'path': request.path}
    return JsonResponse(data)
```
<figcaption>views.py</figcaption>

All we've done is pull attributes from the incoming Django `request` and arranged them in a neat dictionary for our template to decipher. If you recall from our breakdown earlier, we should expect this dictionary to contain:

- **received\_headers** : List of headers receive by requesting client.
- **client\_cookies** : Informat
- **request.path** : The requested URL.

Let's see what our view returns now:

```json
{
  "received_headers": {
    "Content-Length": "",
    "Content-Type": "text/plain",
    "Host": "127.0.0.1:8000",
    "Connection": "keep-alive",
    "Cache-Control": "max-age=0",
    "Sec-Ch-Ua": "Google Chrome 80",
    "Upgrade-Insecure-Requests": "1",
    "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36",
    "Sec-Fetch-Dest": "document",
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
    "Sec-Fetch-Site": "same-origin",
    "Sec-Fetch-Mode": "navigate",
    "Sec-Fetch-User": "?1",
    "Referer": "http://127.0.0.1:8000/",
    "Accept-Encoding": "gzip, deflate, br",
    "Accept-Language": "en-US,en;q=0.9",
    "Cookie": "session=ce55ee3f-03a8-4e18-9c27-ead022a1658e; csrftoken=iwWutpCyivQ5jbIHUdEtXf7XFDWFDEJmAf59jnNM9wjEf4sfvaV9tN5mtrL2Gu; djdt=show"
  },
  "client_cookies": {
      "session": "ce55ee3f-03a8-4e18-9c27-ead022a1658e",
      "csrftoken": "iwWutpCyivQ5jbIHUdEtXf7XFDWFDEJmAf59jnNM9wjEf4sfvaV9tN5mtrL2Gu",
      "djdt": "show"
  },
  "path": "/function_views/get_json"
}
```
<figcaption>What a user sees after hitting <code>get_json_view()</code>.</figcaption>

Holy hell, that's me! You can see quite a bit about me: I'm using Chrome, I'm on a Mac, I came from the homepage, and my CSRF token is... well, let's ignore that part. You can check out a live version of this route here to see what comes for your own headers: [https://django.hackersandslackers.app/function\_views/get\_json/](https://django.hackersandslackers.app/function_views/get_json/)

<!--kg-card-begin: html-->
    Django has a decorator called `@require_http_methods()` we can use to restrict access to views to only honor certain HTTP methods (the same way Flask does)! If we wanted to limit our view to only accept GET requests, our view would look like this:    
```python
from django.http import JsonResponsefrom django.views.decorators.http import require\_http\_methods@require\_http\_methods(["GET"])def get\_json\_view(request): """Return request metadata to the user.""" data = {'received\_headers': dict(request.headers.items()), 'client\_cookies': request.COOKIES, 'path': request.path} return JsonResponse(data)
```

<!--kg-card-end: html-->

## View #2: Dynamic Page Template

Let's take the same idea of displaying a user's client information and apply it in a  user-friendly page template. The main difference between a view serving JSON versus is a page template is the _type of response_ our view returns: where our JSON view returns `JsonResponse`, this view returns `render()`. `render()` accepts three arguments:

1. The incoming `request` object.
2. The page template to serve to the user by specifying a relative filepath to a template.
3. A dictionary of values called `context`.

Understanding **contexts** as they relate to Django page templates is important. When a view (such as `get_template_view()` below) renders a page template, we only have one shot, one opportunity, to pass information to the page before it has to chance to render what we want. In one moment... would you capture it? Or just let it slip?

The good news is it isn't hard to pass information to page templates at all. **context** is a Python dictionary that can accept any number of keys and values, where some of those values could even be lists. Similarly to how we created a dictionary in our last view to serve as JSON, we'll be passing the same information to a page template like so:

```python
from django.shortcuts import render
from django.views.decorators.http import require_http_methods


@require_http_methods(["GET"])
def get_template_view(request):
    """Renders a page template."""
    context = {
        "title": "GET Page Template View",
        "path": request.path
        "received_headers": request.headers.items(),
        "client_cookies": request.COOKIES
    }
    return render(
        request,
        'function_views/get.html',
        context
    )
```
<figcaption>views.py</figcaption>

This time around, we can't simply convert our dictionary into JSON, serve it, and call it a day. We need to analyze the type of data contained in our **context** object and contemplate the best way to display this to fellow human beings.

This is the end result we'll be gunning for while creating our page template, **get.html**:

![Creating Interactive Views in Django](https://hackersandslackers-cdn.storage.googleapis.com/2020/04/django_get_headers_and_cookies_template-copy.jpg)<figcaption><a href="https://django.hackersandslackers.app/function_views/get_template/">https://django.hackersandslackers.app/function_views/get_template/</a></figcaption>

Our **context** dictionary has four keys: **title** , **path** , **received\_headers** , and **client\_cookies**. Of these four keys, we have two different data structure patterns: **title** and **path** are single-value strings, while **received\_headers** and **client\_cookies** are lists of tuples.

For the former, page templates can display the value of any "single-value" key/value pair through the use of double curly (`{{  }}`) brackets. Including  `{{ title }}` and `{{ path }}` in our template renders to `GET Page Template View` and `function_views/get.html` respectively:

```html
{% extends 'layout.html' %}
{% load static %}

<header>
  <h1 class="page-title">{{ title }}</h1>
  <div class="path">{{ path }}</div>
</header>

...
```
<figcaption>get.html</figcaption>

That was the easy part; now we need to render HTML tables from lists of tuples to get our **received\_headers** and **client\_cookies** tables. Luckily Django's templating system allows us to loop through lists programmatically, as so:

```html
{% for header in received_headers %}
    ...
{% endfor %}
```
<figcaption>Loop through a list in a Django page template.</figcaption>

Each `header` in our list of `received_headers` can be used to build rows in our table. Since we're looping through tuples, we can create a 2-column table where the first column contains header _names_ (the first value in our tuple), and the second column contains header _values_ (the second value). We achieve this by breaking each tuple into `{{ header.0 }}` and `{{ header.1 }}`.

```html
...

<table class="header-table">
  <thead>
    <tr>
      <th>Header Key</th>
      <th>Header Value</th>
    </tr>
  </thead>
  <tbody>
    {% for header in received_headers %}
    <tr>
      <td class="key-cell">
        <span>{{ header.0 }}</span>
      </td>
      <td class="value-cell">
        <span>{{ header.1 }}</span>
      </td>
    </tr>
    {% endfor %}
  </tbody>
</table>

...
```

We repeat this again for the client_cookies table, and our page template is complete!

```html
{% extends 'layout.html' %}
{% load static %}

<header>
  <h1 class="page-title">{{ title }}</h1>
  <div class="path">{{ path }}</div>
</header>

{% block content %}
<div class="viewport">
  {% include 'partials/header.html' %}
  <div class="container">

    <!-- Headers received from client. -->
    <div class="table-container headers">
      <h2>Request Headers</h2>
      <table class="header-table">
        <thead>
          <tr>
            <th>Header Key</th>
            <th>Header Value</th>
          </tr>
        </thead>
        <tbody>
          {% for header in received_headers %}
          <tr>
            <td class="key-cell">
              <span>{{ header.0 }}</span>
            </td>
            <td class="value-cell">
              <span>{{ header.1 }}</span>
            </td>
          </tr>
          {% endfor %}
        </tbody>
      </table>
    </div>

    <!-- Cookies received from client. -->
    <div class="table-container cookies">
      <h2>Request Cookies</h2>
      <table class="cookie-table">
        <thead>
          <tr>
            <th>Cookie Property</th>
            <th>Value</th>
          </tr>
        </thead>
        {% for cookie in client_cookies %}
        <tr>
          <td class="key-cell">
            <span>{{ cookie.0 }}</span>
          </td>
          <td class="value-cell">
            <span>{{ cookie.1 }}</span>
          </td>
        </tr>
        {% endfor %}
      </table>
    </div>

  </div>
</div>
{% endblock %}
```
<figcaption>get.html</figcaption>

See it working in action here: [https://django.hackersandslackers.app/functional\_views/get\_template/](https://django.hackersandslackers.app/function_views/get_template/)

## View #3: User-Generated Content

Now that you have your bearings, its time to what any good driving instructor would do to a 16-year old with a learner's permit: force them into a four-lane highway. I'm not sure if you arrived here expecting to walk away with a solid understanding of Django's data layer or form handling, but the only way to learn how to swim is to drown. Or something like that.

Before I bombard you by recklessly blowing through half-explained source code, I need to acknowledge that building interactive experiences (such as those involving forms and databases) comes naturally to _nobody_. We're about to cover a reasonably large amount of information very quickly, and there's a good chance you might get lost or caught up on a few details you feel like you might not understand. This isn't your fault. It's hard to internalize these things all at once, especially when **forms** and **data model ORMs** are topics that could span posts of their own. Don't sweat it.

Anyway, buckle in. We're going 0-100 on a path down nostalgia road to recreate one of the best innovations of 90s internet: the _anonymous guestbook:_

![Creating Interactive Views in Django](https://hackersandslackers-cdn.storage.googleapis.com/2020/04/django-views-guestbook.png)<figcaption><a href="https://django.hackersandslackers.app/function_views/form/">https://django.hackersandslackers.app/function_views/form/</a></figcaption>

For those of you too young to remember the early days of the internet, "guestbooks" were rampant through personal websites belonging to internet communities such as GeoCities, LiveJournal, DeadJournal, etc. They empowered people like Karen from Minnesota to interact with visitors on their site by letting users submit "guestbook" entries in a publicly visible feed. As it turned out, most of Karen's user base often turned out to be malicious bots, trolls, adult-film stars enthusiastic about their live streaming hobby, Nigerian princes, and so forth. In other words, they were cultural melting pots exemplifying the internet's ability to bring people together. Truly beautiful.\*

<!--kg-card-begin: html-->
    ...I am instantly going to regret building this and putting it on the public internet.
<!--kg-card-end: html-->

To build this view, we're going to need two things: a form to accept user input, and a data model representing journal entries. We'll start by creating a form in **forms.py**.

### Creating a Form

Django forms are Python classes that extend Django's `forms.Form` mixin. To add fields to our form, we simply set variables equal to field styles imported from `django.forms` as seen above.

```python
from django import forms
from django.forms import CharField, Textarea, IntegerField


class GuestBookForm(forms.Form):
    name = CharField(
        label="Your name",
        required=True,
        strip=True
    )
    msg = CharField(
        label="Leave a message.",
        required=True,
        widget=Textarea(attrs={'cols': '30', 'rows': '5'}),
        min_length=10
    )
```
<figcaption>forms.py</figcaption>

Django's form creation syntax is pretty easy to understand at first glance. At the risk of going into an endless tangent about forms, I'd encourage you to dissect the subtleties of Django forms on your own accord before I ramble into a tangent about form creation.

### Creating a Data Model

Each guest book "message" will be saved as a record in our database. Each record we create will contain a user's message, the user's name, and creation date. This will serve as the data model for guestbook entries users submit via the form we created:

```python
from django.db import models
from datetime import datetime


class Message(models.Model):
    """Message left by a user in a guest book."""
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=255)
    message = models.TextField()
    created_at = models.DateTimeField()
```
<figcaption>models.py</figcaption>

With your model created, run migrations via **manage.py** to tell Django to create a table for your new model:

```shell
$ python3 manage.py makemigrations
$ python3 manage.py migrate
```

This should automatically recognize the model created in **models.py** and create a table in your SQL database.

### Handling Forms in Function-based Views

From the perspective of a human being, every form we encounter on the web happens in either one of two contexts. We either encounter a form for the first time (an empty form), or we've submitted a form incorrectly, thus bringing us to the form in an incomplete state. The way views determine whether a user is seeing a form for the first time is by looking at the HTTP method sent with the user's request for the page.

Before we worry about logic related to form validation and submission, let's start comfortably by just serving a page with a form on it. Users won't be able to submit it just yet:

```python
from django.shortcuts import render
from django.contrib import messages
from django.http import HttpResponseRedirect

from .forms import GuestBookForm


def form_template_view(request):
    """Handle forms in a function-based view."""
    form = GuestBookForm()
    context = {
        "title": "Form View",
        "form": form,
        "path": request.path
    }
    return render(
        request,
        "function_views/form.html",
        context
    )
```
<figcaption>views.py</figcaption>

Any time you visit a webpage via your browser, you're making a GET request. If users visiting our guestbook page have the GET method as `request.method`, we know they've just arrived and should be served a blank form. If that user were to submit said form, the form would execute a POST request _on the same view_, which is how we know that _these_ users are hitting our view as the result of a form submission attempt. With that knowledge, we can expand on view to handle these two use cases:

```python
...

@require_http_methods(["GET", "POST"])
def form_template_view(request):
    """Handle forms in a function-based view."""
    if request.method == "POST":
        ...
    else:
        form = GuestBookForm()
    context = {
        "title": "Form View",
        "form": form,
        "path": request.path
    }
    return render(
        request,
        "function_views/form.html",
        context
    )
```
<figcaption>views.py</figcaption>

We've added a single `if` statement to see if we're dealing with a submission or not.

Now it's time for the magic. The first thing that happens in our `if request.method == "POST":` block is a check to see if the form is valid by comparing the user's submitted form values to the validators we put in place when we created **forms.py**.

We'll assume the form was filled correctly, which brings us to the miracle of life itself: data creation. We create a new record in our database by extracting data from each of our form's fields and passing them into our Message model ( `Message.objects.create()` ). Each "field" we set in our model is defined via positional arguments in `.create()`, where we pass the name and message from our form:

```python
...

from .models import Message


@require_http_methods(["GET", "POST"])
def form_template_view(request):
    """Create data records via form submission."""
    if request.method == "POST":
        form = GuestBookForm(request.POST)
        if form.is_valid():
            Message.objects.create(name=form.cleaned_data.get("name"),
                                   message=form.cleaned_data.get("msg"))
            messages.success(request, "Success!")
            return HttpResponseRedirect("form")
    else:
        form = GuestBookForm()
    context = {
        "title": "Form View",
        "form": form,
        "path": request.path,
        "entries": Message.objects.all()
    }
    return render(
        request,
        "function_views/form.html",
        context
    )
```
<figcaption>views.py</figcaption>

Last but not least, we add one more key/value pair to our **context** dictionary called `entries`, like so:

```python
{ ... 'entries': Message.objects.all()}
```
<figcaption>Fetching all records of a database model. </figcaption>

Calling `.objects.all()` on any database model returns all database records associated with a model. Here we're passing all messages that users have left in our guestbook, so that we can display them to users. Check out the working version for yourself, but try to keep your comments nice as I have very fragile emotions: [https://django.hackersandslackers.app/function\_views/form/](https://django.hackersandslackers.app/function_views/form/)

## View #4: Dynamic URLs

In the days of ye olde internet, we mostly referred to entities on the web as _websites_ as opposed to _web apps_. If there's a distinction to be made here, I'd argue that the term _website_ implied a certain expectation of functionality/structure: _websites_ were typically structured as defined sets of pages that served dynamic content. _Web apps_ contrast this by flipping the paradigm: the structure of a web "app" is typically informed by the data layer itself, which heightens web applications to be a reflection of the data which drives them.

_An_ obvious example of this would be the concept of user profile pages. The taxonomy of a social network is entirely dependent on the users who have created profiles, groups, and all the other things which make a social network a social network. If we were to launch an identical clone of Facebook's codebase with ten active users per month, it would be a drastically different entity than the Facebook we know with 2.5 billion active users per month.

To illustrate the concept of apps with page structures determined by data, I threw together a fake social network of my own. It currently has 11 users, each of whom  happen to be amongst the greatest musical artists of the 21st century:

![Creating Interactive Views in Django](https://hackersandslackers-cdn.storage.googleapis.com/2020/04/django_tutorial_user_profiles_2.gif)<figcaption>Navigating pages with dynamic URLs.</figcaption>

### Creating Dynamic URL Patterns

Why is this important, and why am I showing this to you? The magic lays within the concept of dynamic URL patterns. User profile URLs in the above example are structured as `/users/1/` `/users/2/` and so forth. The number in this URL represents the user's assigned **user ID**. To see how this URL pattern differs from the ones we've created thus far, let's take a look at **urls.py** :

```python
from django.urls import path

from . import views

urlpatterns = [
    # URL patterns of views we've created thusfar
    path('get_json/', views.get_json_view, name='get_json'),
    path('get_template/', views.get_template_view, name='get_template'),
    path('form/', views.form_template_view, name='form'),
    
    # URL patterns to handle dynamic pages
    path('users/', views.user_profile_list_view, name='users'),
    path('users/<int:user_id>/', views.user_profile_view),
]
```
<figcaption>urls.py</figcaption>

Unlike our other URLs, the last URL pattern is able to accept a dynamic value with `'users/<int:user_id>/'`. The presence of `<int:user_id>` tells Django to honor _any integer_ in our URL, which means visiting a URL such as `/users/4563988/` would be perfectly valid. But what if we don't have a user with an ID equal to **4563988**? I'm glad you asked - let's see what's happening in `user_profile_view()` in **views.py**.

### Resolving Views with Dynamic Values

Unlike views we defined previously, `user_profile_view()` now accepts _a second_ parameter, which happens to be **user\_id** being passed in through our URL pattern:

```python
...
from .models import User
from django.shortcuts import get_object_or_404


@require_http_methods(["GET"])
def user_profile_view(request, user_id):
    """User profile page."""
    user = get_object_or_404(User, id=user_id)
    context = {
        "user": user,
        "title": f"{user.name}\'s Profile",
        "path": request.path
    }
    return render(
        request,
        "function_views/profile.html",
        context
    )
```
<figcaption>views.py</figcaption>

Our view is surprisingly simple thanks in part to a magic function called `get_object_or_404()`. This function allows us to query a data model for a single record, similar to calling `User.objects.get(id=user_id)`. The difference between fetching a record directly via the **User** model versus `get_object_or_404()` is the latter method instantly serves the user a 404 if record does not exist:

```python
# get_object_or_404([MODEL], [FIELD]=[VALUE])
get_object_or_404(User, id=user_id)
```
<figcaption>Fetch a single record where id is equal to user_id, otherwise 404.</figcaption>

The above is how we know which user's profile to serve at a given URL. The sequence of events looks like this:

- A user navigates to the URL **/users/1/**.
- Our URL pattern passes `1` to our view, `user_profile_view()`.
- `user_profile_view()` attempts to fetch a user from the database matching an ID of `1`.
- If a user matches the given ID, the record for that user is passed to our profile template to generate a frontend profile for Cardi B.
- If no user matches the given ID, Django serves a 404 error.

Check out the functionality in action for yourself: [https://django.hackersandslackers.app/function\_views/users/](https://django.hackersandslackers.app/function_views/users/)

## View #5: Query String Parameters

A great way to pass contextual information to views is through the use of [query string parameters](https://en.wikipedia.org/wiki/Query_string). Let's say we wanted a way to filter the list of users in our users view to only display users matching certain criteria. An easy solution would be the ability to append arguments to our path like so:

```shell
http://127.0.0.1:8000/function_views/users/?profession=rapper
```
<figcaption>Query string to filter our list of users to return only rappers.</figcaption>

Fetching parameters in a view is actually quite easy. Parameters are stored in the `request` object, specifically for `GET` requests. The syntax for fetching a parameter by name from our URL looks like this:

```python
param = request.GET.get("parameter_name")
```
<figcaption>Fetch query string parameters from an incoming request.</figcaption>

The above checks to see if a parameter called **parameter\_name** was appended to the URL submitted by the user. If so, our variable will be equal to the value assigned to **parameter\_name**.

We're going to modify our user list view to look for a parameter named **profession** in the URL. If it exists, we'll filter the results by whichever profession value was provided. Otherwise, we'll just display all users:

```python
...


@require_http_methods(["GET", "POST"])
def user_profile_list_view(request):
    """Directory of user profiles."""
    user_profession = request.GET.get("profession", None)
    if user_profession:
        users = User.objects.filter(profession=user_profession)
    else:
        users = User.objects.all()
    context = {
        "title": "User Profile Directory",
        "users": users,
        "path": request.path
    }
    return render(
        request,
        "function_views/users.html",
        context
    )
```
<figcaption>views.py</figcaption>

Let's see it in action:

![Creating Interactive Views in Django](https://hackersandslackers-cdn.storage.googleapis.com/2020/04/django_views_query_string-1.gif)<figcaption>Filtering a list via a query string.</figcaption>

## I Need to Pass Out Now

When I first decided to write an intro tutorial to Django views, I never would have expected it to take over a month of effort and 5000 words of jargon. This underscores a fact I thought I fully understood: Django is not an easy framework to deal with by today's standards. There are a lot of things I was forced to skim over in this tutorial, which is something that pains me to do. Don't blame yourself if some of this stuff hasn't clicked, because it isn't your fault. I'm here to help - that's what the comments section is for :).

{% github https://github.com/hackersandslackers/django-views-tutorial %}