A Django FotoBlog in VS Code — First Views

How To Create A FotoBlog in VS Code — Part IV— DjangoSeries Episode # 18

J3
Jungletronics
15 min readApr 21, 2023

--

Hi,

This is a continuation of this post.

In the end I hope this article has helped you to gain a better understanding about Django concepts and get this page ready to code!

Here is what you can get reading this post! Codeply Warm up!

This is our brainstorming!

Made with excalidraw 🥰️

Designing a website using Django involves several steps. Here’s a general overview of the process:

  1. Set up a development environment: Before you can start building your website, you need to set up a development environment that includes Django. You can use a virtual environment to manage dependencies and isolate your project from other Python installations on your system;
  2. Create a new Django project: Once you have a development environment set up, you can create a new Django project using the command-line tool provided by Django;
  3. Define your website’s models: Models are the building blocks of your website’s data. You’ll need to define the models that represent the types of data you want to store, such as blog posts or user profiles;
  4. Create views and templates: Views handle the logic for each page of your website, while templates define the structure and appearance of those pages. You’ll need to create views and templates for each page of your website;
  5. Define URL patterns: URL patterns map URLs to specific views in your Django project. You’ll need to define URL patterns for each page of your website;
  6. Set up a database: Django provides several options for managing databases, including SQLite, MySQL, and PostgreSQL. You’ll need to choose a database and set it up to work with your Django project;
  7. Test and debug: Once you’ve built your website, it’s important to test it thoroughly and debug any issues you find;
  8. Deploy your website: Finally, you’ll need to deploy your website to a server so that it’s accessible to the public. This typically involves configuring a web server and setting up a database server, as well as configuring any other necessary services (such as SSL);

On the whole, building a website in Django requires a good understanding of Python and web development principles, as well as familiarity with Django’s architecture and tools. However, with practice and perseverance, you can create a powerful and dynamic website using Django.

It has rhythm, balance and diversity, but we could standardize our procedures, and it’s our commitment to what we want the journey to be about as we move on through these Pure Django Series.

Which is part of Django's design philosophy?

Don't repeat yourself (DRY)

Every distinct concept and/or piece of data should live in one,
and only one, place. Redundancy is bad. Normalization is good.
The framework, within reason, should deduce as much as possible
from as little as possible.

We’ll keep continuing to do that!

Let’s update our simple application by using the console and simple html outputs.

KISS!

Fine let’s thinking about authentication:

Modern applications tend to follow some common practices when it comes to authentication and registration procedures. Here are a few tendencies:

  1. Social Login: Social login or OAuth allows users to sign in to an application using their social media accounts, such as Facebook, Google, or Twitter. It simplifies the sign-up process for users, as they do not need to create a new account and remember new credentials.
  2. Two-Factor Authentication: Two-factor authentication (2FA) is becoming more popular as it adds an extra layer of security to the authentication process. It requires users to enter a code sent to their mobile phone or email, in addition to their password, to access their account.
  3. Passwordless Login: Passwordless authentication methods like biometric authentication or one-time codes sent via email or SMS are gaining popularity, as they eliminate the need for users to remember and manage passwords.
  4. Progressive Profiling: Progressive profiling is a method where applications gather user information over time instead of requiring all the information upfront. It simplifies the sign-up process for users and increases the likelihood that they will complete it.
  5. Privacy and Consent: With the increasing emphasis on data privacy, modern applications tend to provide clear and concise information on how user data will be used and obtain explicit consent from users before collecting or sharing their data.

In general, modern applications tend to prioritize ease of use, security, and privacy in their authentication and registration procedures, while trying to minimize friction and simplify the process for users.

Django-allauth is a popular third-party package for authentication and account management in Django.

I have published a Django-allauth Tutorial recently…

Let’s apply it here right now as the app in still simple.

Please, follow step by step of this post:

00#Step — Open fotoblog/settings.py and type:

import os
from pathlib import Path

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-74v@f*me_p&mpabqs_f8_70r*2z^jjh_jjnm*=7b_ps&d$xch%'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []

SITE_ID = 1

# Provider specific settings
SOCIALACCOUNT_PROVIDERS = {
'github': {
# For each OAuth based provider, either add a ''SocialApp''
# (''socialaccount'' app) containing the required client
# credentials, or list them here:
'APP': {
'client_id': '<your ID>',
'secret': '<Your Key>',
'key': ''
}
}
}

AUTHENTICATION_BACKENDS = [

'django.contrib.auth.backends.ModelBackend',
'allauth.account.auth_backends.AuthenticationBackend',
]


# Application definition

INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'blog.apps.BlogConfig',
'authenticate.apps.AuthenticateConfig',
'django.contrib.sites',

'allauth',
'allauth.account',
'allauth.socialaccount',
'allauth.socialaccount.providers.github',
]

MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'fotoblog.urls'

TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / 'templates'],
'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',
],
},
},
]

WSGI_APPLICATION = 'fotoblog.wsgi.application'


# Database
# https://docs.djangoproject.com/en/3.2/ref/settings/#databases

DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}


# Password validation
# https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]


# Internationalization
# https://docs.djangoproject.com/en/3.2/topics/i18n/

LANGUAGE_CODE = 'en-us'

# TIME_ZONE = 'UTC'
TIME_ZONE = 'America/Porto_Velho'

USE_I18N = True

USE_L10N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.2/howto/static-files/
# Static -> App Native (css, js, ico ...)
# Media -> Outside origin (profile_imgs, Logos ...)

# STATIC_ROOT = os.path.join(BASE_DIR, "static")
STATIC_URL = '/static/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR, "static"),
]

MEDIA_ROOT = os.path.join(BASE_DIR, "media")
MEDIA_URL = '/media/'

# Default primary key field type
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

AUTH_USER_MODEL = 'authenticate.User'

LOGIN_REDIRECT_URL = 'dashboard'
LOGOUT_REDIRECT_URL = 'account_login'

EMAIL_BACKEND = 'django.core.mail.backends.filebased.EmailBackend'
EMAIL_FILE_PATH = BASE_DIR / 'emails'

Note: about allauth please see this post

This is a configuration file for a Django project, where various settings related to the project are defined. Some notable parts of the code include:

  • BASE_DIR: the base directory of the project, which is used to construct other file paths relative to this directory;
  • SECRET_KEY: a secret key that is used for cryptographic signing and should be kept secret in production;
  • SOCIALACCOUNT_PROVIDERS: variable with a dictionary of keys that correspond to each social provider;
  • django.contrib.sites: This is a Django framework that provides a way to associate objects with particular sites, which is useful for multi-site applications.
  • allauth: This is a third-party Django library that provides a flexible and customizable system for user authentication and authorization.
  • allauth.account: This module provides views and templates for handling user account management, including registration, login, password reset, and email verification.
  • allauth.socialaccount: This module provides integrations with various third-party authentication providers, such as GitHub, Facebook, Twitter, and Google.
  • allauth.socialaccount.providers.github: This module provides an integration specifically for authenticating users with GitHub accounts.
  • INSTALLED_APPS: a list of the installed applications in the project. These include both Django's built-in apps and any custom apps you've created;
  • DIRS': [BASE_DIR / 'templates'] : tells Django to also look for templates in the templates directory located in the project's base directory, in addition to the templates directories in any installed applications.

By default, Django includes a set of validators in AUTH_PASSWORD_VALIDATORS, including:

  • UserAttributeSimilarityValidator: checks if the password is too similar to user attributes (e.g. username, email).
  • MinimumLengthValidator: checks if the password meets a minimum length requirement.
  • CommonPasswordValidator: checks if the password is a commonly used password.
  • NumericPasswordValidator: checks if the password contains numeric values.

In a Django project’s settings file, the STATIC_URL and STATICFILES_DIRS settings are used to configure the handling of static files.

  • STATIC_URL is a string that defines the base URL for serving static files. In the given configuration, STATIC_URL = '/static/' sets the base URL to /static/, meaning that any static files requested from the server with a URL starting with /static/ will be served from the project's static file directory.
  • STATICFILES_DIRS is a list of directories where Django should look for static files. In the given configuration, STATICFILES_DIRS is set to [os.path.join(BASE_DIR, "static")], which is a list containing a single directory called static located within the project's base directory. This tells Django to also look for static files in the static directory located in the project's base directory, in addition to any static files in any installed applications
  • This configuration assumes that the project has a directory called static in the project's base directory, which contains static files such as CSS, JavaScript, and image files that are used across the project. By default, Django's development server will serve static files automatically during development, but in production, it is recommended to use a separate server or a content delivery network (CDN) to serve static files for better performance and security.
  • DATABASES: a dictionary containing the database configuration for the project. In this case, it is set to use the SQLite database engine;
  • STATICFILES_DIRS: a list of directories containing static files for the project. These files are served directly by Django's development server in development mode, but should be served by a separate web server like Apache or Nginx in production;
  • MEDIA_ROOT and MEDIA_URL: the root directory and URL for media files (e.g. user-uploaded files) served by the project;
  • AUTH_USER_MODEL: the custom user model used in the project, which is defined in the authenticate app; please read this from stackoverflow.
  • LOGIN_REDIRECT_URL and LOGOUT_REDIRECT_URL: the URLs to redirect to after a successful login or logout, respectively;
  • EMAIL_BACKEND and EMAIL_FILE_PATH: the backend and file path used for sending email in the project. In this case, emails are written to a file in the project's root directory.

01#Step — Now Open fotoblog/urls.py and type:

from django.contrib import admin
from django.urls import path, include
from django.shortcuts import render

urlpatterns = [
path('admin/', admin.site.urls),
path('accounts/', include('allauth.urls')),
# path('accounts/', include('django.contrib.auth.urls')),
path('', include('blog.urls')),
]

In our code, we are including several URL patterns:

  • The first one maps the URL /admin/ to the Django admin site, which is used to manage the data in your Django project.
  • The second one includes the URL patterns provided by the allauth library, which is used for user authentication and registration.
  • The third one is commented out and includes the URL patterns provided by Django’s built-in authentication system (previous episode).
  • The fourth one maps the root URL (i.e., the empty string) to the blog app's URL patterns, which define the URLs and views for your blog app.

Overall, this code sets up the basic URL routing for your Django project, including the admin site, user authentication, and our blog app.

02#Step — Now Open blog/urls.py and type:

from django.urls import path
from blog import views

urlpatterns = [
path('', views.home, name='dashboard'),
path('photos/', views.photos),

]

This is the urlpatterns list in a Django app's urls.py file. It defines the mapping between URLs and view functions that handle them.

In this specific example, there are two URL patterns defined:

  • The empty string ('') is mapped to the home view function from the views module of the blog app. The name dashboard is given to this URL pattern;
  • The string 'photos/' is mapped to the photos view function from the views module of the blog app.
  • This URLs are included in fotoblog/urls.py.

Note that these URL patterns are relative to the root URL configuration defined in the project’s urls.py file. So if the project's urls.py file maps the root URL 'blog/' to this app, then the URL pattern for the home view function would be 'blog/' followed by the name 'dashboard'. But in our case we map to the root URL '/' (empty string), all we need is just an empty URL pointing to the root to access our dashboard.

<host>:<port>/
http://127.0.0.1:8000/

This is minimalist don’t you think?

03#Step — Designing our dashboard:

<html>

<head></head>

<body>

<div>Sidebar</div>

<div>
Dashboard
{% block content tag goes here! %}
{% endblock %}
</div>

</body>

<footer></footer>

</html>

This is the base template for our website idea with a sidebar, header, footer, and a main content section with a placeholder for content (name dashboard).

Here is our real implementation base.html:

{% load socialaccount %}
{% load static %}
<html>
<head>
<!-- Bootstrap CDN -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">

<!-- CSS -->
<link rel="stylesheet" type='text/css' href="{% static 'css/base.css' %}">

<title>FotoBlog</title>

</head>
<body>
<div class="sidebar">
<h1>FotoBlog</h1>

<p>Hey {{request.user}} !</p>

<!-- Img Goes Here! -->
<img class="w-32 h-32 md:h-auto rounded-full" src="{% static 'imgs/logo.png' %}">

<p><a href="{% url 'dashboard' %}">Home</a></p>
<p><a href="#">Photo Feed</a></p>
<p><a href="#">Create Blog Post</a></p>
<p><a href="#">Upload Photo</a></p>
<p><a href="#">Upload Multiple Photos</a></p>
<p><a href="#">Follow Users</a></p>
<p><a href="#">Change Profile Photo</a></p>
<p><a href="{% url 'account_reset_password' %}">Reset Password</a></p>
<p><a href="{% url 'account_logout' %}">Log Out</a></p>
</div>
<div class="main">
<p>Dashboard</p>
{% block content %}
{% endblock content %}
</div>

<!-- Option 1: Bootstrap Bundle with Popper -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
</body>
{% include 'blog/footer.html' %}
</html>

The {% url %} template tag in Django is used to generate URLs for named URL patterns in your Django application. This allows you to easily create links in your HTML templates without hard coding the URLs.

We implement three links:

The first link, <a href=”{% url ‘dashboard’ %}”>Home</a>, is pointing to the URL pattern named 'dashboard'. When a user clicks on this link, Django will redirect them home page.

The second link, <a href="{% url 'account_reset_password' %}">Reset Password</a>, is pointing to the URL pattern named 'account_reset_password'. When a user clicks on this link, Django will redirect them to the corresponding view function that is responsible for resetting the user's password.

The third link, <a href="{% url 'account_logout' %}">Log Out</a>, is pointing to the URL pattern named 'account_logout'. When a user clicks on this link, Django will redirect them to the corresponding view function that will log the user out of their current session.

The {% block content %} and {% endblock %} tags are used to define a block that can be overridden by child templates.

This means that any template that extends this base template can override the content section by defining a block with the same name and adding their own content.

For our example, we had a template called dashboard.html that extends this base template like this:

{% extends 'blog/base.html' %}
{% block content %}
<h5>
PHOTOS GO HERE!
</h5>
{% endblock content %}

In this example, the extends tag tells Django that this template extends the base.html template.

The {% block content %} tag defines a block with the same name as the one in the base.html template.

The content between the {% block %} and {% endblock %} tags will override the content in the base.html template when this template is rendered.

04#Step — Now Css

Bootstrap is a popular front-end web development framework that provides pre-designed CSS styles and JavaScript plugins to help developers create responsive, mobile-first web pages more easily and quickly.

To use bootstrap in your web page, you would need to include the bootstrap CSS and JavaScript files and use bootstrap classes and components in your HTML code.

We did it, but for now, for sake of simplicity, let’s use css to make a sidebar.

In the root directory create a folder called static and put inside 3 new dirs (css, ico and imgs):

satic/css, /ico and /js

Paste this base.css (enjoy and save your logo too):

html {
font-family: Arial;
}

/* The sidebar menu */
.sidebar {
height: 100%; /* Full-height: remove this if you want "auto" height */
width: 190px; /* Set the width of the sidebar */
position: fixed; /* Fixed Sidebar (stay in place on scroll) */
z-index: 1; /* Stay on top */
top: 0; /* Stay at the top */
left: 0;
background-color: #1d3030;
color: #ffffe6;
overflow-x: hidden; /* Disable horizontal scroll */
padding-top: 15px;
padding-left: 10px;
padding-right: 10px;
}

/* The navigation menu links */
.sidebar a {
text-decoration: none;
color: #ffffe6;
display: block;
}

/* When you mouse over the navigation links, change their color */
.sidebar a:hover {
color: #f1f1f1;
}

/* Style page content */
.main {
margin-left: 210px; /* Same as the width of the sidebar */
padding: 20px;
}

.avatar {
width: 80%;
border-radius: 10%;
display: block;
margin: auto;
padding-top: 10px;
padding-bottom: 10px;
}

.grid-container {
display: grid;
grid-template-columns: auto auto auto;
padding: 20px;
text-align: center;
}

.post img {
width: 80%;
border-radius: 5px;
}

05#Step — To work work static file you need tag tamplate and link to .css file like this (we already did it in the previous step):

{% load static %}
<html>
<head>
<!-- Bootstrap CDN -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">

<!-- CSS -->
<link rel="stylesheet" type='text/css' href="{% static 'css/base.css' %}">
...

And in your fotoblog/settings.py set:

...
# Static -> App Native (css, js, ico ...)
# Media -> Outside artifact (profile_imgs, Logos ...)

# STATIC_ROOT = os.path.join(BASE_DIR, "static")
STATIC_URL = '/static/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR, "static"),
]

MEDIA_ROOT = os.path.join(BASE_DIR, "media")
MEDIA_URL = '/media/'
...

06#Step — Now go to blog/views.py:

from django.http import HttpResponse
from django.shortcuts import render
# from django.contrib.auth.decorators import login_required

# @login_required()


def home(request):
return render(request, 'blog/dashboard.html')

# @login_required()


def photos(request):
return HttpResponse('Photos')

07#Step — The last step, inside templates /blog/ create a footer.html, paste:

<footer class="text-center text-lg-start bg-light text-muted">                    
<!-- Copyright -->
<div class="text-center text-dark p-3" style="background-color: rgba(95, 93, 93, 0.2);">
© 2023 Copyright:
<a class="text-dark" href="https://medium.com/jungletronics">jungletronics</a>
</div>
<!-- Copyright -->
</footer>

To load it into any page .html just include the file like we did in base.html:

<html>    
....

</body>

{% include 'blog/footer.html' %}

</html>

08#Step — Now let’s deal with all-Auth urls:

These are the Django-AllAuth URLs. Note that the names are prefixed with [name=account_something]. The directory where these HTMLs are hosted is also called account. But the translation of the URL is accounts/… with final s 🤔️. These nuances took me a while to understand…🤯️
At first, in the previous episode, without AllAuth, we implement all these URLs. To make these tags work just edit settings.py (remove the note you make to ‘DIRS’: [BASE_DIR / ‘templates’] forcing Django to go to the default which is the registration directory — I left all the HTMLs for you to test). Another cool test is to rename the Dir account, to for example fake, and reset the settings.py . So Django will uses Views and URLs from the Django-AllAuth library (link).

09#Step —Go to my drive and save 12 HTMLs templates into templates/account directory.

Download it from this link

Here are the result pages:

10#Step — Navigating through the new site (consider to see this graph):

Logged out 🙃️
Almost logged in using third party account from GitHub…by click Continue
…I am Logged in with GitHub Oauth 2 🥰️
Resetting password (already logged…) or by logging out giljr…
Hit Request Password Reset button…
Message email sent…Our custom template Password Reset Done. Now check email; remember we are using [EMAIL_BACKEND = ‘django.core.mail.backends.filebased.EmailBackend’ EMAIL_FILE_PATH = BASE_DIR / ‘emails’)]
We need to click on this link. Here is the email for your enjoyment…
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Subject: [FotoBlog] Password Reset E-mail
From: webmaster@localhost
To: giljr.2009@gmail.com
Date: Fri, 21 Apr 2023 17:21:50 -0000
Message-ID: <168209771072.31569.13140859441262358339@JAYTHREE>

Hello from FotoBlog!

You're receiving this e-mail because you or someone else has requested a password for your user account.
It can be safely ignored if you did not request a password reset. Click the link below to reset your password.

http://127.0.0.1:8000/accounts/password/reset/key/6-bn0d4e-8985cd20dbf5e555b1179fa9b77b07b1/

In case you forgot, your username is giljr.

Thank you for using FotoBlog!
medium.com/jungletronics
-------------------------------------------------------------------------------
🎉️🎉️🎉️TA-DA! Email Received!🎉️🎉️🎉️
Password Reset Confirm.
Password resetted!
Log in with username and password instead of GitHub token.
There you have DB Schema!
Compare with admin dashboard. Remember? Django Framework deduce as much as possible
from as little as possible. Yeah that’s cool! 😉️ Django Rocks it! 🦮️

That’s all folks!

Let’s recap:

Forth Episode Tutorial - Django Series - Step-by-step list:

1-BrainStorming The Project with https://excalidraw.com/

2-Designing a website - General overview;

3-Describing common practices when it comes to authentication and registration procedures

4-Modifying fotoblog/settings.py;

5-Editing fotoblog/urls.py adn blog/urls.py

6-Working with Templates

7-Testing the Authentication System via Provider GiHub

In the next episode let’s work with forms in Django. See you there!

Bye for now!

👉️GitHub

👉️Google Drive

References & Credits

Create a Web Application With Django by openclassrooms.com

Code with Stein — Python Django Social Authentication | Django AllAuth Tutorial

DBeaver Community 23.0.0 by dbeaver.io

A Django Blog In VS Code — Quick Start! by jungletronics

https://docs.djangoproject.com/en/4.1/topics/auth/default/#using-the-django-authentication-system

LoginView — Classy CBV

pennersr/django-allauth

👌️Time Zones List— Django

Django Allauth

Django using get_user_model vs settings.AUTH_USER_MODEL

https://bootsnipp.com/snippets/7np2j

Tagging:

Note: run in one line in the terminal.

git tag -a Episode.04 -m "FotoBlog - v1.0: from http://jungletronics.com" -m "Forth Episode Tutorial - Django Series - Step-by-step list:" -m "1-BrainStorming The Project with https://excalidraw.com/" -m "2-Designing a website - General overview;" -m "3-Describing common practices when it comes to authentication and registration procedures" -m "4-Modifying fotoblog/settings.py;" -m "5-Editing fotoblog/urls.py adn blog/urls.py" "6-Working with Templates" "7-Testing the Authentication System via Provider GiHub" 

git push origin Episode.04

Note: on GitHub Repo click tag. I am using Python 3.7.6 Django 3.2.18.

Nave-Gate-Tuts-Parts: I . II . III . IV . V . VI . VII . VIII . IX . X

--

--

J3
Jungletronics

Hi, Guys o/ I am J3! I am just a hobby-dev, playing around with Python, Django, Ruby, Rails, Lego, Arduino, Raspy, PIC, AI… Welcome! Join us!