SEI-Example
  • Introduction
  • About These Notes
  • Syllabus
  • Development Workflow
    • Installfest
      • Mac OSX
      • Linux
      • Git Configuration
      • Sublime Packages
    • Command Line
      • The Terminal
      • Filesystem Navigation
      • File Manipulation
      • Additional Topics
    • Intro to Git
      • Version Control
      • Local Git
      • Remote Git
      • Git Recipes
    • Group Collaboration
      • Git Workflows
      • Project Roles and Tools
    • VS Code Tips & Tricks
  • HTML/CSS
    • HTML
    • CSS Selectors
    • CSS Box Model and Positioning
      • Box Model
      • Display and Positioning
      • Flexbox
      • Grid
      • Flexbox & Grid Games
      • Floats and Clears
      • Additional Topics
    • Advanced CSS
      • Responsive Design
      • Pseudo-Classes/Elements
      • Vendor Prefixes
      • Custom Properties
      • Additional Topics
    • Bootstrap
    • CSS Frameworks
    • Accessibility
  • JavaScript
    • Primitives
    • Arrays
    • Objects
    • Control Flow
      • Boolean Expressions
      • Conditionals
      • Loops
      • Promises
    • Functions
      • Callbacks
      • Timing Functions
      • Iterators
    • DOM and Events
    • DOM Manipulation
    • HTML5 Canvas
    • How To Reduce Redundancy
    • (2019) JavaScript OOP
    • (2016) OOP with Classes
    • (1995) OOP with Prototypes
      • Constructors
      • Prototypes
    • Intro to TDD
    • Scoping
    • Inheritance
      • Prototypal Inheritance
      • Call, Apply, and other Functions
      • ES6 Inheritance
      • Resources
    • Custom Node Modules
    • Additional Topics
      • AJAX, Fetch, and Async/Await
      • AJAX w/JSON and Localstorage
        • AJAX w/JSON
        • Local Storage
      • Async module
      • Data Scraping
  • jQuery
    • Intro
      • DOM Manipulation
      • Reddit Practice
      • Styling
      • Events
    • Plugins
    • AJAX
  • APIs
    • Fetch
    • AJAX w/jQuery
    • AJAX w/Fetch
  • Databases
    • Intro to SQL
    • Advanced SQL
    • MongoDB
      • Intro to NoSQL
      • CRUD in MongoDB
      • Data Modeling
      • Intermediate Mongo
  • Node/Express
    • Node
      • Intro to Node
      • Node Modules
      • Node Package Manager (NPM)
    • Express
      • Intro to Express
        • Routes
        • Views
        • Templates
        • Layouts and Controllers
        • CRUD & REST
          • Get and Post
          • Put and Delete
      • APIs with Express (request)
      • APIs with Express (axios)
    • Sequelize
      • Terminology
      • Setup
      • Using Models
      • Seeding Data
      • Validations and Migrations
      • Resources
      • 1:M Relationships
      • N:M Relationships
    • Express Authentication
      • Research Components
      • Code Components
      • Auth in Theory
        • Sessions
        • Passwords
        • Middleware
        • Hooks
      • Auth in Practice
        • Create the User
        • User Signup
        • Sessions
        • User Login
        • Authorization and Flash messages
    • Testing with Mocha and Chai
    • Mongoose
      • Mongoose Associations
    • JSON Web Tokens
      • Codealong
    • Additional Topics
      • oAuth
      • Geocoding with Mapbox
      • Geocoding and Google Maps
      • Cloudinary
      • Websockets with Socket.io
      • SASS
  • Ruby
    • Intro to Ruby
    • Ruby Exercises
    • Ruby Classes
    • Ruby Testing with Rspec
    • Ruby Inheritance
    • Ruby Data Scraping
  • Ruby on Rails
    • Intro to Rails
    • APIs with Rails
    • Asset Pipeline
    • Rails Auth and 1-M
      • Auth Components
    • Rails N:M
    • ActiveRecord Polymorphism
    • Additional Topics
      • oAuth
      • SASS
      • Rails Mailers
      • Cloudinary
      • Jekyll
  • React (Updated 2019)
    • ES6+/ESNext
      • Const and Let
      • Arrow Functions
      • Object Literals and String Interpolation
      • ES6 Recap
      • ES6 Activity
    • Intro to React
      • Create React App
      • Components and JSX
      • Virtual DOM
      • Props
      • Dino Blog Activity
      • Nested Components
      • Lab: LotR
    • React State
      • Code-Along: Mood Points
      • Code-Along: Edit Dino Blog
      • Lab: Simple Calc
      • Lifting State
    • React Router
      • Browser History/SPAs
      • React Router (lesson and full codealong)
      • Router Lab
    • Fetch and APIs
      • APIs with Fetch and Axios
      • Fetch the Weather
    • React Hooks
    • React LifeCycle
      • Lab: Component LifeCycle
    • React Deployment
    • Additional Topics
      • React Frameworks
        • Material UI Theming
      • Typescript
        • More Types and Syntax
        • Tsconfig and Declaration Files
        • Generics with Linked List
      • Redux
      • TypeScript
      • Context API
      • React Native
  • Meteor
  • Deployment and Config
    • Deploy - Github Pages
    • Deploy - Node/Sequelize
    • Deploy - Node/MongoDB
    • Deploy React
    • Deploy - Rails
      • Foreman (Environment Variables)
    • Deploy - AWS Elastic Beanstalk
    • Deploy - S3 Static Sites
    • Deploy - Django
    • Deploy - Flask
  • Data Structures and Algorithms
    • Recursion
    • Problem Solving - Array Flatten
    • Binary Search
    • Algorithm Complexity
    • Stacks and Queues
    • Bracket Matching
    • Ruby Linked Lists
      • Sample Code
      • Beginner Exercises
      • Advanced Exercises
    • JS Linked Lists
      • Sample Code
      • Beginner Exercises
      • Beginner Solutions
    • Hash Tables
    • Intro to Sorting
    • Insertion Sort
    • Bucket Sort
    • Bubble Sort
    • Merge Sort
    • Quick Sort
    • Heap Sort
    • Sorting Wrapup
    • Hashmaps
    • Trees and Other Topics
  • Python
    • Python Installation
    • Intro to Python
    • Python Lists
    • Python Loops
    • Python Dictionaries
    • Python Sets and Tuples
    • Python Cheatsheet
    • Python Functions
    • Python Classes
    • Python Class Inheritance
    • Intro to Flask
    • Intro to SQLAlchemy
      • Flask and SQLAlchemy
    • Using PyMongo
    • Intro to Django
    • CatCollector CodeAlong
      • URLs, Views, Templates
      • Models, Migrations
      • Model Form CRUD
      • One-to-Many Relations
      • Many-to-Many Relations
      • Django Auth
    • Django Cheatsheet
    • Django Auth
    • Django Polls App Tutorial
    • Django School Tool Tutorial
    • Django 1:M Relationships
    • Custom Admin Views
    • Data Structures and Algorithms
      • Recursion
      • Binary Search
      • Stacks and Queues
      • Linked Lists
      • Binary Trees
      • Bubble Sort
      • TensorFlow & Neural Networks
    • Adjacent Topics
      • Raspberry Pi
      • Scripting
  • Assorted Topics
    • History of Computer Science
    • Regular Expressions
    • Intro to WDI (Course Info)
    • Being Successful in WDI
    • Internet Fundamentals
      • Internet Lab
    • User Stories and Wireframing
      • Wireframing Exercise: Build an Idea
    • Post WDI
      • Learning Resources
      • Deliverables -> Portfolio
      • FAQ
  • Projects
    • Project 1
    • Project 2
    • Project 3
      • Project 3 Pitch Guidelines
    • Project 4
    • Past Projects
      • Project 1
      • Project 2
      • Project 3
      • Project 4
      • Portfolios
    • Post Project 2
    • MEAN Hackathon
      • Part 1: APIs
      • Part 2: Angular
    • Portfolio
  • Web Development Trends
  • Resources
    • APIs and Data
    • Tech Websites
    • PostgreSQL Cheat Sheet
    • Sequelize Cheat Sheet
    • Database Administration
  • Archived Section
    • (Archived) ReactJS
      • Intro to React
        • Todo List Codealong
        • Additional Topics
      • Deploy React
      • React with Gulp and Browserify
        • Setting up Gulp
        • Additional Gulp Tasks
      • React Router
        • OMDB Router
        • OMDB Search
        • Additional Resources
      • React Animations
        • CSS Animations
    • AngularJS
      • Intro to AngularJS
        • Components and SPA
        • Create an Angular App
      • Angular Directives and Filters
      • Angular Animation
      • Angular Bootstrap Directives
        • Bootstrap Modals
      • Angular $http
      • Angular Services
        • Service Recipes
        • ngResource
        • Star Wars Codealong
      • Angular Routing
      • Angular + Express
      • Angular Authentication
        • Additional Topics
      • Angular Components
      • Angular Custom Filters
      • Angular Custom Directives
Powered by GitBook
On this page
  • Django Authentication
  • Learning Objectives
  • Django Local Session Authentication
  • Adding Users
  • How do we login?
  • Log Out!
  • Signup
  • Protecting Routes
  • Seeing the user's cats
  • Conclusion
  • Further Reading
  1. Python
  2. CatCollector CodeAlong

Django Auth

Django Authentication

Learning Objectives

  1. Explain what is meant by local session authentication

  2. Add the Django User model to an app

  3. Implement the common auth pages (signup, login, logout, profile)

  4. Protect routes from unauthorized access

Django Local Session Authentication

It's important to understand the flow of how a user is authenticated in a web app. There are many ways to do it and we will learn a few over the cohort. What Django uses is called local session authentication. The local part means that the authentication, (the validation of a username and password), happens in our own server. We store the password and we do the checking to see if it matches. The session part refers to how the server keeps track of who is logged in and what they have access to. The session is just another object stored in memory. Usually it only contains a user's ID but any additional information can be stored in the session object.

Django has a great pre-built authentication module that we can use but it is still good to understand what is happening:

Signing up a New User

One of the simpler views that we will write, basically all this does is check to see if a user credential (like a user name) is available and, if it is, it creates a new user record with an encrypted password and stores it in the database. This action typically also logs the new user in.

Logging In

Once a user is created, they are able to log in. This is done via a login form that is sent to the user. The user fills it out and it is sent to Django where the submitted password is checked against the one in the database. If it matches, a new session is created in the server. This session is the "proof" that a user has been authorized. Each time that user sends a new request to the server, it is sent with a session cookie that allows the server to look up that user's session. As long as the session cookie is present and neither it nor the session have reached their expiration date, the user should continue to have access.

Logging Out

This is as simple as deleting the session. We do this in Django through a simple logout() function. Once the session is gone, the user must create a new one and the only way to do that is by successfully logging in and having your credentials verified.

Though this may sound complicated, much of it is hidden from us by the power of the Django framework. Let's see how we put this together:

Adding Users

In main_app/models.py, let's include Django's built-in User model from their auth library:

  from django.contrib.auth.models import User

While we are adding in the User Model, we can actually add the relationship to the Cat model so each user can have their own Cat Collection! What fun! We saw how one-to-many relationships worked yesterday. We need to add a foreign key to the Cat and set it as the User. This establishes the 1:m relationship:

  # main_app/models.py (in Cat model)
  ...
  user = models.ForeignKey(User, on_delete=models.CASCADE)
  ...

We should now run the python3 manage.py makemigrations command to integrate our foreign key. We will get a prompt from Django asking for one of two options. You should see something like this:

You are trying to add a non-nullable field 'user' to a cat without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:
 1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
 2) Quit, and let me add a default in models.py
Select an option:

Let's choose option 1.

This will create a 'dummy' field of User that will be populated with null value row for us. We want this.

It will ask you one more time to enter a default value:

Please enter the default value now, as valid Python
The datetime and django.utils.timezone modules are available, so you can do e.g. timezone.now
Type 'exit' to exit this prompt

Go ahead and enter the number 1. This will set a row to simply 1. This will then trigger a migration file creation called XXXX_cat_user.py. Excellent!

Now run the migration by running:

python3 manage.py migrate

The reason this happened is because we effectively added a column to our table that holds our Cats. We put a new attribute called user into the model and this created a column in the table with no data in it. Django was asking us what we wanted to put in there since a foreign key field usually isn't allowed to be blank. We told it to just use the number 1 which will link all of our cats to user number 1, which is probably your superuser account.

The one cool thing that we can see immediately is in our admin interface: We can now assign Cats a user value! Play with our Admin view and bask in the joy of being able to create Users and assigning Users to Cats! Go ahead and assign all of your Cats if you like.

Now go to your main_app/views.py file and lets update our view for the cats:

# main_app/views.py
...
class CatCreate(CreateView):
    model = Cat
    fields = ['name', 'breed', 'description', 'age']

    def form_valid(self, form):
        self.object = form.save(commit=False)
        self.object.user = self.request.user
        self.object.save()
        return HttpResponseRedirect('/cats/' + str(self.object.pk))
...

In our CatCreate form, we need to change our list of fields to leave out the user's ID. Otherwise, we won't be able to submit the form. I've only listed the fields relevent to adding a new cat. Then we can use the form_valid() function to add in the user's ID pulled from the request object. This way, we always add the ID of the user currently logged in to this session.

How do we login?

To implement a user login system, we'll follow the pattern of URL, Form, View, then Template.

In main_app/urls.py add the login route:

...
path('login/', views.login_view, name="login")
...

Let's add the login_view function in main_app/views.py:

...
# Add LoginForm to this line...
from django.contrib.auth.forms import AuthenticationForm
# ...and add the following line...
from django.contrib.auth import authenticate, login, logout
...
def login_view(request):
     # if post, then authenticate (user submitted username and password)
    if request.method == 'POST':
        form = AuthenticationForm(request, request.POST)
        if form.is_valid():
            u = form.cleaned_data['username']
            p = form.cleaned_data['password']
            user = authenticate(username = u, password = p)
            if user is not None:
                if user.is_active:
                    login(request, user)
                    return HttpResponseRedirect('/user/'+u)
                else:
                    print('The account has been disabled.')
            else:
                print('The username and/or password is incorrect.')
    else: # it was a get request so send the emtpy login form
        form = AuthenticationForm()
        return render(request, 'login.html', {'form': form})

Finally, we'll add a new file for the login.html template:

<div data-gb-custom-block data-tag="extends" data-0='base.html'></div>

<div data-gb-custom-block data-tag="block"></div>

<h1>Login</h1>
<form method="POST" action=".">
  

<div data-gb-custom-block data-tag="csrf_token">

  {{ form.as_p}}
  <input type="submit" value="Submit" />
</form>

</div>

Go ahead and test out the login route! I hope you remembered one of your user's passwords...

Log Out!

This will be a similar pattern of URL and view, but no form or template.

In main_app/urls.py:

...
path('logout/', views.logout_view, name="logout"),
...

Create the corresponding main_app/views.py logout_view function:

...
def logout_view(request):
    logout(request)
    return HttpResponseRedirect('/cats')
...

Finally, let's add the log in and log out links to our site. Let's add it to our base.html since we want it to be accessible from every view. Add these lines under the link for "View All My Cats":

    <header>
      

<div data-gb-custom-block data-tag="if"></div>

      <span><a href="

<div data-gb-custom-block data-tag="url" data-0='profile'></div>">Hello, {{ user.username }}!</a></span>
      <span> | </span>
      <span><a href="<div data-gb-custom-block data-tag="url" data-0='logout'></div>">Logout</a></span>
      <span> | </span>
      <div data-gb-custom-block data-tag="else"></div>

      <span><a href="

<div data-gb-custom-block data-tag="url" data-0='login'></div>">Login</a></span>
      <span> | </span>
      <span><a href="<div data-gb-custom-block data-tag="url" data-0='signup'>">SignUp</a></span>
      </div>

      <a href="/cats">
          <h1>catcollector</h1>
      </a>
    </header>
    <hr />

Awesome! Now we have login and logout functionality and the ability to see if you're currently logged in!

Signup

In main_app/urls.py add a signup url:

path('signup/', views.signup, name='signup'),

In main_app/views.py add a signup view function:

# add this line
from django.contrib.auth.forms import UserCreationForm

# add this function
def signup(request):
    if request.method == 'POST':
        form = UserCreationForm(request.POST)
        if form.is_valid():
            user = form.save()
            login(request, user)
            return redirect('home')
    else:
        form = UserCreationForm()
        return render(request, 'signup.html', {'form': form})

In templates add a new file signup.html:

<div data-gb-custom-block data-tag="extends" data-0='base.html'></div>

<div data-gb-custom-block data-tag="block"></div>

  <h2>Sign up</h2>
  <form method="post">
    

<div data-gb-custom-block data-tag="csrf_token">

    {{ form.as_p }}
    <button type="submit">Sign up</button>
  </form>

</div>

Protecting Routes

Now that we have nicely authenticated users accessing the site, we should look at limiting access to some of our routes. There are many different and complicated ways to do this in Django, but the easiest is probably to use decorators. These are statements that we put before functions or classes to change their characteristics. The decorators we will be using are login_required and method_decorator.

In main_app/views.py add this :

# Import both decorators
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
...

# Above the class CatDelete, add this line
@method_decorator(login_required, name='dispatch')
class CatDelete(DeleteView):
...

# Above the profile function, add this line
@login_required
def profile(request, username):
...

Let's look at this code. We import both of the decorators and then we use the @method_decorator to protect our generic editing view for deleting cats. This is the one to use when protecting a class-based view. We tell the method_decorator what action we want (login_required in this case) and we also pass in the name of the method to protect (the dispatch method.) The dispatch() method is what sends the data from our class to the browser. It's normally hidden but by referring to it by name here we can effectively prevent our form from sending anything out if someone isn't logged in.

The @login_required decorator is what we use on plain view functions (the kind that start with def). It does basically the same thing - you must be logged in to view someone's profile.

Now deleting a cat and viewing a user's profile actually require you to authenticate! We have protected our cats!

What other views should we protect?

Seeing the user's cats

Now we need to add a view to see a User's profile. This will repeat the same, familiar pattern:

  1. Set up a URL in urls.py

  2. Create a view in views.py

  3. Make an HTML template in /templates

Let's go to main_app/urls.py folder and update our urlpatterns:

# main_app/urls.py
...
path('user/<username>/', views.profile, name='profile'),
...

The stuff in the angle brackets lets us grab a passed-in username and store it in a variable called username. Let's add to our main_app/views.py file:

...
# add this line
from django.contrib.auth.models import User
...
# add this function
def profile(request, username):
    user = User.objects.get(username=username)
    cats = Cat.objects.filter(user=user)
    return render(request, 'profile.html', {'username': username, 'cats': cats})

Lastly, let's create a profile.html template to show a single User and all of the Cats they have collected:

<div data-gb-custom-block data-tag="extends" data-0='base.html'></div>

<div data-gb-custom-block data-tag="block">

<h1>{{ username }}'s collection:</h1>

<div data-gb-custom-block data-tag="for">

<a href="/{{ cat.id }}">
  <h3>{{ cat.name }}</h3>
</a>

</div>

</div>

Let's also update our cats/index.html page to allow us to inspect each user. Put this below the closing <div> in the card:

<!-- main_app/templates/index.html -->
<a href="/user/{{cat.user.username}}">
    <p>Adopted By: {{cat.user.username }}</p>
</a>

Conclusion

That was the whirlwind tour of Django authentication. There is a lot more you could spend time learning about authentication but this takes care of what you will need to meet project requirements. Django makes the process very simple. Authentication is incredibly important in web development. We make public-facing, data-aware products that can contain very sensitive information. Authentication helps us protect that information as well as protecting our server or clients from being used in larger cyber attacks. I urge you all to learn as much as you can about security.

Further Reading

PreviousMany-to-Many RelationsNextDjango Cheatsheet

Last updated 3 years ago

Read more about the

cleaned_data
MDN Docs/Tutorial for implementing the built-in authentication in Django 3.1
OWASP Top 10 Security Concerns
OWASP Session Management