django
web-framework
python
django
is a popular framework
settings.py
, admin.py
and models.py
as needed along the waypip3 install django
python3 -m django --version
django-admin startproject django_blog_app
cd django_blog_app
django_blog_app #project root dir
|- django_blog_app #project's root app
| |-__init__.py
| |-settings.py
| |-urls.py
| |-wsgi.py
|- manage.py
pip3 install virtualenv virtualenvwrapper
python3 -m venv django_blog_app
source django_blog_app/bin/activate
pip install --upgrade pip
pip3 install django
pip3 install Pillow
pip3 install django-crispy-forms
python3 manage.py runserver 6500
# default port is 8000
127.0.0.1:6500
on your browser
localhost:6500
Ctrl+C
in terminalstartapp
command|- <new-app-name>
| |- __init__.py
| |- admin.py
| |- apps.py
| |- migrations/__init__.py
| |- models.py
| |- tests.py
| |- views.py
|- db.sqlite3
python3 manage.py startapp blog
python3 manage.py startapp users
django_blog_app
|- django_blog_app
|- blog
|- users
|- manage.py
|- db.sqlite3
the registration is done in the django_blog_app/settings.py
file
apps.py
file to get class-name slug
class
es
# blog/apps.py
class BlogConfig(AppConfig):
name = 'blog'
# users/apps.py
class UsersConfig(AppConfig):
name = 'users'
'<app-name-in-apps.py-class>.apps.<class-name-in-apps.py>',
INSTALLED_APPS
entry in settings.py
file, add the following lines:
'blog.apps.BlogConfig',
'users.apps.UsersConfig',
INSTALLED_APPS
list in settings.py
enables the apps’ models being picked up by djangodatabase migrations have to be applied before creating a superuser
superuser account is needed to access the django admin page that sits @ localhost:6500/admin
the very first DB migration creates the DB and adds a few default tables
auth_user
is one such table that needs to be initialized before creating the superuser accountpython3 manage.py makemigrations
python3 manage.py migrate
models.py
to the DBauth_user
table is created when above is run the first timepython3 manage.py createsuperuser
localhost:6500/admin/
to verify they workpython3 manage.py shell
in the python shell:
from django.contrib.auth.models import User
User.objects.filter(is_superuser=True)
usr = User.objects.get(username=<superuser-name-output-above>)
usr.set_password('<new-password>')
usr.save()
startapp
,
models.py
is automatically created in the app dirUser
model is automatically created when the project is created
from django.contrib.auth.models import User
Post
model:
Post
model in blog/models.py
:
# blog/models.py
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
class Post(models.Model):
title = models.CharField(max_length = 100)
content = models.TextField()
date_posted = models.DateTimeField(default = timezone.now)
author = models.ForeignKey(User, on_delete = models.CASCADE)
Profile
model:
User
class ORM model
Profile
model in user/models.py
:
# user/models.py
from django.db import models
from django.contrib.auth.models import User
class Profile(models.Model):
user = models.OneToOneField(User, on_delete = models.CASCADE)
image = models.ImageField(default='default.jpg',upload_to='profile_pics')
admin.py
file
Post
model in blog/admin.py
file
# blog/admin.py
from django.contrib import admin
from .models import Post
admin.site.register(Post)
Profile
model in users/admin.py
file
# users/admin.py
from django.contrib import admin
from .models import Profile
admin.site.register(Profile)
python3 manage.py makemigrations
python3 manage.py migrate
Profile
model is not automatically created when a new User
account is createdProfile
creation and updation that follows the User
model, do the following:
signals.py
in users
app dir, i.e. users/signals.py
# users/signals.py
from django.db.models.signals import post_save
from django.contrib.auth.models import User
from django.dispatch import receiver
from .models import Profile
@receiver(post_save, sender = User)
def create_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user = instance)
@receiver(post_save, sender = User)
def save_profile(sender, instance, **kwargs):
instance.profile.save()
ready
function to users/apps.py
under the class UsersConfig(...)
:
# users/apps.py
class UsersConfig(AppConfig):
name = 'users'
def ready(self):
import users.signals
templates are HTML files for the final render view
urls.py
filethe routing is handled in the project globally and each app locally in the urls.py
file
urls.py
filesettings.py
file, we can do this# settings.py
...
import os
...
TEMPLATES = [
{
...
'DIRS': [os.path.join(BASE_DIR, 'templates')],
...
},
]
templates
dir in the project root dir
django_blog_app
|- django_blog_app
|- blog
|- users
|- templates
|- manage.py
|- db.sqlite3
forms.py
forms.py
is not for applying styles, but to handle the forms backendurls.py
needs a view handlerso first, we will look at how to do this for the home page
views.py
: add the following lines
# blog/views.py
#import built in class based view
from django.views.generic import ListView
# import the model Post from models.py
from .models import Post
# Create your views here.
class PostListView(ListView):
model = Post
urls.py
and add the following code
# blog/urls.py
from .views import PostListView
urlpatterns = [
path('',PostListView.as_view(), name="blog-home")
]
routing
include
imports in the project urls.py
file
blog.urls
to the urlpatterns
list with include
as follows
# urls.py
from django.urls import path, include
urlpatterns = [
...
path('',include('blog.urls'))
]
base.html
in the project-level templates
folderblog
in the same dir
post_list.html
base.html
file with the Bootstrap 4 Starter Template
models.py
views.py
templates/<app-dir>
localhost:6500
in your browsercrispy-forms is a plugin to apply bootstrap styling to forms automatically
third party django app that makes working with forms easier
settings.py
...
INSTALLED_APPS = [
'crispy_forms',
...
]
...
settings.py
at the very end of file, add following
CRISPY_TEMPLATE_PACK = 'bootstrap4'
UserCreationForm
form-object exists with in django to generate the form necessary for new-user-creationdjango.contrib.auth.forms
form-view
forms.py
file with the UserCreationForm
# users/forms.py
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
class UserRegisterForm(UserCreationForm):
email = forms.EmailField()
class Meta:
model = User
fields = [
'username',
'email',
'password1',
'password2',
]
view
# users/views.py
from django.shortcuts import render, redirect
from django.contrib import messages
from .forms import UserRegisterForm
# Create your views here.
def register(request):
if request.method == 'POST':
form = UserRegisterForm(request.POST)
if form.is_valid():
form.save()
return redirect('blog-home')
else:
form = UserRegisterForm()
return render(request, 'users/register.html', {'form':form})
templates/users
dir to hold the user registration template
register.html
routing
urls.py
has the following lines:
from django.contrib import admin
from django.urls import path, include
from users import views as user_views
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('blog.urls')),
path('register/', user_views.register, name='register')
]
template
then add the following code into templates/users/register.html
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="container">
<form method="POST">
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom">
Join Today
</legend>
{{ form | crispy }}
</fieldset>
<div class="form-group">
<button class="btn btn-primary" >
Sign Up
</button>
</div>
</form>
</div>
{% endblock content %}
load @localhost:6500/register
in browser
view
which is set in the views.py
file
in users/views.py
setup these lines of code
...
if form.is_valid():
form.save()
# to generate message
username = form.cleaned_data.get('username')
messages.success(request, "Account created for {}".format(username))
return redirect('blog-home')
...
template
in the base template, a placeholder has to be set for showing flash messages
in templates/base.html
, add the following logic:
...
</header>
{% if message %}
{% for message in messages %}
<div class="alert alert-{{ message.tags }}">
{{ message }}
</div>
{% endfor %}
{% endif %}
{% block content %}
...
routing/view
import these views in the project urls.py
directly
# urls.py
...
from django.contrib.auth import views as auth_views
urlpatterns = [
...
path('login/',auth_views.LoginView.as_view(), name='login')
path('logout/',auth_views.LogoutView.as_view(), name='logout')
]
these views are Class-based views
registration
template
registration
in templateslogin.html
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="container">
<form method="POST">
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom">
Log In
</legend>
{{ form | crispy }}
</fieldset>
<div class="form-group">
<button class="btn btn-primary" >
Login
</button>
</div>
</form>
<div class="form-group">
<small class="text-muted">
Need an account? <a href="{% url 'register' %}"> Sign Up Now </a>
</small>
</div>
</div>
{% endblock content %}
then, in settings.py
, configure the default value of login redirect url
# settings.py
LOGIN_REDIRECT_URL = 'blog-home'
routing
add the following path in the project urls.py
# urls.py
...
urlpatterns = [
...
path('logout/',auth_views.LogoutView.as_view(template_name="registration/logout.html"), name='logout'),
]
template
template_name
has to be specified because the default logout redirect page is the admin panel logout
logout.html
template existscreate the logout.html
file in the templates/registration
folder with the following code
{% extends "base.html" %}
{% block content %}
<div class="container">
<h2 class="mt-5">You've been logged out</h2>
<div class="form-group">
<small class="text-muted">
Want to login? <a href="{% url 'login' %}"> Login </a>
</small>
</div>
</div>
{% endblock content %}
template
in the register.html
...
<div class="form-group">
<small class="text-muted">
Need an account? <a href="{% url 'login' %}"> Sign Up Now </a>
</small>
</div>
...
set up a conditional check in the base template nav bar section
user
global variable
is_authenticated
that allows to check if user is logged intemplate
use the following code bit to setup this conditional logic in base.html
...
<ul class="navbar-nav mr-auto">
{% if user.is_authenticated %}
<li class="nav-item">
<a class="nav-link" href="{% url 'logout' %}">Logout</a>
</li>
{% else %}
<li class="nav-item">
<a class="nav-link" href="{% url 'login' %}">Login</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'register' %}">Register</a>
</li>
{% endif %}
</ul>
...
routing
urls.py
# urls.py
...
urlpatterns = [
...
path('profile/', user.views.profile, name="profile")
]
view
users/views.py
# users/views.py
...
from django.contrib.auth.decorators import login_required
...
@login_required
def profile(request):
return render(request, 'users/profile.html')
...
template
then create the template file users/profile.html
{% extends "base.html" %}
{% block content %}
<div class="container p-5">
<img class="img-fluid" src="{{ user.profile.image.url }}" alt="user display image">
<div class="text-center">
<h2> {{ user.username}} </h2>
<p> {{ user.email }} </p>
</div>
</div>
{% endblock content %}
then tell django the login url in the settings.py
# settings.py
...
LOGIN_URL = 'login'
@login_required
decorator looks for the login page in a dir named /accounts/login.html
bu default
template
base.html
, add the following logic...
{% if user.is_authenticated %}
<li class="nav-item">
<a class="nav-link" href="{% url 'profile' %}">Profile</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'logout' %}">Logout</a>
</li>
{% else %}
...
the following strategy is for development time to store images
settings.py
, configure the following
# settings.py
...
MEDIA_ROOT = os.path.join(BASE_DIR,'media')
MEDIA_URL = '/media/'
upload_to
parameter in the Profile model will be saved torouting
urls.py
# urls.py
...
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
...
]
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root = settings.MEDIA_ROOT)
/media/default.jpg
model
# users/models.py
from django.db import models
from PIL import Image
from django.contrib.auth.models import User
# Create your models here.
class Profile(models.Model):
user = models.OneToOneField(User, on_delete = models.CASCADE)
image = models.ImageField(default='default.jpg', upload_to='profile_pics')
def save(self,*args, **kwargs):
super().save(*args, **kwargs)
img = Image.open(self.image.path)
if img.height > 300 or img.width > 300:
output_size = (300,300)
img.thumbnail(output_size)
img.save(self.image.path)
the POST-GET-REDIRECT pattern is used to setup the profile update system
form/view
# users/forms.py
...
from .models import Profile
...
class UserUpdateForm(forms.ModelForm):
email = forms.EmailField
class Meta:
model = User
fields = ['username', 'email']
class ProfileUpdateForm(forms.ModelForm):
class Meta:
model = Profile
fields = ['image']
view
users/views.py
as follows:
# users/views.py
...
from .forms import UserRegisterForm, UserUpdateForm, ProfileUpdateForm
...
@login_required
def profile(request):
if request.method == "POST":
user_form = UserUpdateForm(request.POST, instance=request.user)
profile_form = ProfileUpdateForm(request.POST, request, FILES, instance=request.user.profile)
if user_form.is_valid() and profile_form.is_valid():
user_form.save()
profile_form.save()
messages.success(request, 'Account Updated!')
return redirect('profile')
user_form = UserUpdateForm(instance = request.user)
profile_form = ProfileUpdateForm(instance = request.user.profile)
context = {
'user_form': user_form,
'profile_form': profile_form,
}
return render(request, 'users/profile.html', context)
template
then, update the profile template templates/users/profile.html
as follows
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="container p-5 d-flex flex-column align-center align-items-center ">
<img class="img-fluid" src="{{ user.profile.image.url }}" alt="user display image">
<div class="text-center">
<h2> {{ user.username}} </h2>
<p> {{ user.email }} </p>
</div>
</div>
<hr>
<form class="p-5" method="POST" enctype="multipart/form-data">
{% csrf_token %}
<fieldset class="form-group">
<legend>
Profile Info
</legend>
{{ user_form | crispy }}
{{ profile_form | crispy }}
</fieldset>
<button class="btn btn-primary w-100" type="submit">
Update
</button>
</form>
{% endblock content %}
routing
setup the routing for the detail view as follows
# blog/urls.py
...
from .views import PostListView, PostDetailView
urlpatterns = [
...
path('post/<int:pk>/', PostDetailView.as_view(), name='post-detail'),
]
view
# blog/views.py
...
from django.views.generic import (
ListView,
DetailView,
)
...
class PostDetailView(DetailView):
model = Post
template
object
in the template context by default
object
variable directly links to the instance of the model classtemplates/blog/post_detail.html
which the .as_view()
part looks for
{% extends "base.html" %}
{% block content %}
<article class="p-3">
<img class="img-fluid" src="{{ object.author.profile.image.url }}" alt="author-image">
<div >
<div>
<a href="">
{{ object.author }}
</a>
<small>
{{ object.date_posted | date:"Y, F d" }}
</small>
</div>
<div class="my-3">
{% if object.author == user %}
<a href="{% url 'post-update' object.id %}"> <button class="btn btn-info"> Update Post </button> </a>
<a href="{% url 'post-delete' object.id %}"> <button class="btn btn-danger"> Delete Post </button> </a>
{% endif %}
</div>
<h2> {{object.title}} </h2>
<p> {{object.content}} </p>
</div>
</article>
{% endblock content %}
define the create view in the blog app
view
# blog/views.py
...
from django.views.generic import (
ListView,
DetailView,
CreateView,
UpdateView,
)
from djnago.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
# Create your views here.
...
# create view
class PostCreateView(LoginRequiredMixin, CreateView):
model = Post
fields = [
'title',
'content',
]
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
# update view
class PostUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
model = Post
fields = [
'title',
'content',
]
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
def test_func(self):
post = self.get_object()
if self.request.user == post.author:
return True
else: return False
routing
# blog/urls.py
...
from .views import PostListView, PostCreateView, PostDetailView, PostUpdateView
urlpatterns = [
...
path('post/new/', PostCreateView.as_view(), name='post_create'),
path('post/<int:pk>/update', PostUpdateView.as_view(), name='post-update'),
]
template
templates/blog/post_form.html
post_form
because is a form for the post model{% extends "base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="container">
<form method="post">
{% csrf_token %}
<fieldset class="form-group">
<legend class="">
Blog Post Compose
</legend>
{{ form | crispy }}
</fieldset>
<div class=form-group>
<button class="btn btn-success" type="submit" >
Post
</button>
</div>
</form>
</div>
{% endblock content %}
base.html
file...
{% if user.is_authenticated %}
<li class="nav-item">
<a class="nav-link" href="{% url 'post-create' %}"> New Post </a>
</li>
...
templates/blog/post_detail.html
, add the following logic:
...
<div class="my-3">
{% if object.author == user %}
<a href="{% url 'post-update' object.id %}"> <button class="btn btn-info"> Update Post </button> </a>
{% endif %}
</div>
<h2> {{object.title}} </h2>
...
redirection
after post has been created, the user has to be redirected to the post detail view
get_absolute_url
method in the post modelsetup a get_absolute_url
method in the post model in blog/models.py
# blog/models.py
...
from django.urls import reverse
# Create your models here.
class Post(models.Model):
...
def get_absolute_url(self):
return reverse('post-detail', kwargs={'pk':self.pk})
/post/new
nowrouting
should already exist in the blog/urls.py
file by now
# blogs/urls.py
...
from .views import PostListView, PostCreateView, PostDetailView
urlpatterns = [
path('', PostListView.as_view(), name="blog-home"),
...
]
view
the view should be handled in the blog/views.py
file as follows
# blog/views.py
...
from django.views.generic import (
ListView,
...
)
...
class PostListView(ListView):
model = Post
context_object_name = 'posts'
ordering = ["-date_posted"]
...
template
templates/blog/post_list.html
which the .as_view()
part looks for the list view{% extends "base.html" %}
{% block content %}
{% for post in posts %}
<div class="card m-5">
<div class="card-title m-3">
<a href="{% url 'post-detail' post.id %}" > <h1> {{ post.title }} </h1> </a>
<p> {{ post.author }} </p>
</div>
<div class="card-body">
<p> {{ post.content }} </p>
</div>
</div>
{% endfor %}
{% endblock content %}
routing
urls.py
file# blog/urls.py
...
from .views import PostListView, PostCreateView, PostDetailView, PostUpdateView, PostDeleteView
urlpatterns = [
...
path('post/<int:pk>/delete', PostDeleteView.as_view(), name='post-delete'),
]
view
# blog/views.py
...
from django.views.generic import (
ListView,
DetailView,
CreateView,
UpdateView,
DeleteView,
)
...
...
# delete view
class PostDeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
model = Post
success_url = '/'
def test_func(self):
post = self.get_object()
if self.request.user == post.author:
return True
else: return False
template
create the file templates/blog/post_confirm_delete.html
which the .as_view()
part looks for
{% extends "base.html" %}
{% block content %}
<div class="container">
<form method="post">
{% csrf_token %}
<fieldset class="form-group">
<legend class="">
Delete Post
</legend>
<h2> Are you sure you want to delete the post "{{ object.title }}"? </h2>
</fieldset>
<div class=form-group>
<button class="btn btn-danger" type="submit" >
Yes, Delete!
</button>
<a class="btn btn-secondary" href="{% url 'post-detail' object.id %}"> Cancel </a>
</div>
</form>
</div>
{% endblock content %}
add delete button in the post detail view
templates/blog/post_detail.html
add the following lines:...
{% if object.author == user %}
<a href="{% url 'post-delete' object.id %}"> <button class="btn btn-danger"> Delete Post </button> </a>
{% endif %}
...
paginate_by
attribute is set in ListView’s class based view
# blog/views.py
...
class PostListView(ListView):
model = Post
...
paginate_by = 2 # sets the number of blog posts per page
in templates/blog/post_list.html
, add the following pagination logic
{% if is_paginated %}
<div class="container align-center d-flex flex-row justify-content-center">
{% if page_obj.has_previous %}
<a class="btn btn-outline-info" href="?page=1">First</a>
<a class="btn btn-outline-info" href="?page={{page_obj.previous_page_number}}">Previous</a>
{% endif %}
{% for num in page_obj.paginator.page_range %}
{% if page_obj.number == num %}
<a class="btn btn-info" href="?page={{num}}"> {{num}} </a>
{% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %}
<a class="btn btn-outline-info" href="?page={{num}}"> {{num}} </a>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<a class="btn btn-outline-info" href="?page={{page_obj.next_page_number}}">Next</a>
<a class="btn btn-outline-info" href="?page={{page_obj.paginator.num_pages}}">Last</a>
{% endif %}
</div>
{% endif %}
view
# blog/views.py
from django.shortcuts import render, get_object_or_404
...
from django.contrib.auth.models import User
...
# user filter for post list view
class UserPostListView(ListView):
model = Post
template_name = '/blog/user_posts.html'
context_object_name = 'posts' # sets the context variable name inside the template being called
paginate_by = 2
def get_queryset(self): # overrides get
user = get_object_or_404(User, username=self.kwargs.get('username'))
return Post.objects.filter(author=user).order_by('-date_posted')
...
routing
# blog/urls.py
...
from .views import PostListView, ..., UserPostListView
urlpatterns = [
...
path('user/<str:username>/', UserPostListView.as_view(), name='user-posts'),
]
template
templates/blog/user_posts.html
and add the following logic to it{% extends "blog/base.html" %}
{% block content %}
<h1> Posts by {{view.kwargs.username}} </h1>
<p> ({{page_obj.paginator.count }} posts by this user) </p>
{% for post in posts %}
<article class="media content-section">
<img class="rounded-circle" src="{{post.author.profile.image.url}}" style="max-width: 90px; margin: 3px">
<div class="media-body">
<div class="article-metadata">
<a class="mr-2" href="{% url 'user-posts' post.author.username %}">{{ post.author }}</a>
<small class="text-muted">{{ post.date_posted | date:"Y, F d" }}</small>
</div>
<h2><a class="article-title" href="{% url 'post-detail' post.id %}">{{ post.title }}</a></h2>
<p class="article-content">{{ post.content }}</p>
</div>
</article>
{% endfor %}
{% if is_paginated %}
{% if page_obj.has_previous %}
<a class="btn btn-outline-info" href="?page=1">First</a>
<a class="btn btn-outline-info" href="?page={{page_obj.previous_page_number}}">Previous</a>
{% endif %}
{% for num in page_obj.paginator.page_range %}
{% if page_obj.number == num %}
<a class="btn btn-info" href="?page={{num}}"> {{num}} </a>
{% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %}
<a class="btn btn-outline-info" href="?page={{num}}"> {{num}} </a>
{% endif %}
{% endfor %}
{% if page_obj.has_previous %}
<a class="btn btn-outline-info" href="?page={{page_obj.next_page_number}}">Next</a>
<a class="btn btn-outline-info" href="?page={{page_obj.previous_page_number}}">Last</a>
{% endif %}
{% endif %}
{% endblock content %}
settings.py
routing/view
add following path to project urls.py
# urls.py
...
urlpatterns = [
...
path('password-reset/', auth_views.PasswordResetView.as_view(template_name = 'users/password_reset.html'), name = "password_reset"),
]
template
create templates/users/profile_reset.html
file with following logic in it
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="container my-5">
<form method="POST">
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom">
Reset Password
</legend>
{{ form | crispy }}
</fieldset>
<div class="form-group">
<button class="btn btn-primary" >
Request Password Reset
</button>
</div>
</form>
</div>
{% endblock content %}
routing/view
add following path to project urls.py
# urls.py
...
urlpatterns = [
...
path('password-reset-confirm/<uidb64>/<token>/', auth_views.PasswordResetConfirmView.as_view(template_name = 'users/password_reset_confirm.html'), name = "password_reset_confirm"),
]
template
create templates/users/profile_reset_confirm.html
file with following logic in it
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="container my-5">
<form method="POST">
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom">
Reset Password
</legend>
{{ form | crispy }}
</fieldset>
<div class="form-group">
<button class="btn btn-primary" >
Reset Password
</button>
</div>
</form>
</div>
{% endblock content %}
routing/view
add following path to project urls.py
# urls.py
...
urlpatterns = [
...
path('password-reset/done', auth_views.PasswordResetDoneView.as_view(template_name = 'users/password_reset_done.html'), name = "password_reset_done"),
]
template
create templates/users/profile_reset_done.html
file with following logic in it
{% extends "base.html" %}
{% block content %}
<div class="container">
<div class="alert alert-info">
An email has been sent with instructions to reset your password.
</div>
</div>
{% endblock content %}
settings.py
ethereal
Username
and Password
settings.py
and add the following lines at the end
# SMTP Configuration
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.ethereal.email'
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST_USER = '<ethereal-account-username>'
EMAIL_HOST_PASSWORD = '<ethereal-account-app-password>'
gmail
settings.py
and add the following lines at the end
# SMTP Configuration
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST_USER = '<gmail-account-username>'
EMAIL_HOST_PASSWORD = '<gmail-account-app-password>'
routing/view
add following path to project urls.py
# urls.py
...
urlpatterns = [
...
path('password-reset-complete/', auth_views.PasswordResetCompleteView.as_view(template_name = 'users/password_reset_complete.html'), name = "password_reset_complete"),
]
template
create templates/users/profile_reset_complete.html
file with following logic in it
{% extends "base.html" %}
{% block content %}
<div class="container my-5">
<div class="alert alert-info">
Your password has been reset!
</div>
<a href="{% url 'login' %}" class="my-3"> Sign in here </a>
</div>
{% endblock content %}
web: gu nicorn django_blog_app.wsgi
settings.py
ALLOWED_HOSTS = ['django-blog-nmc.herokuapp.com']
settings.py
STATIC_ROOT = os.path.join(BASE_DIR,'staticfiles')
to launch app on heroku
heroku
heroku login
pip3 install gunicorn
pip3 freeze > requirements.txt
heroku create django-blog-nmc
git push heroku master
heroku open
Shift + Cmd + P
menu by pressing those buttons> Docker Add
Docker: Add docker file to workspace
Python: Django
as the application platformmanage.py
as the app’s entry point8000
for the exposed port (8000
is the default value)No
for including docker compose filerequirements.txt
requirements.txt
file using pip3 freeze > requirements.txt
add the following lines in a file named Dockerfile
situated in the project root dir
# For more information, please refer to https://aka.ms/vscode-docker-python
FROM python:3.8-slim-buster
# Set app-listen port
EXPOSE 8000
# Keeps Python from generating .pyc files in the container
ENV PYTHONDONTWRITEBYTECODE=1
# Turns off buffering for easier container logging
ENV PYTHONUNBUFFERED=1
# Install pip requirements
ADD requirements.txt .
RUN python -m pip install -r requirements.txt
WORKDIR /app
ADD . /app
# Switching to a non-root user, please refer to https://aka.ms/vscode-docker-python-user-rights
RUN useradd appuser && chown -R appuser /app
USER appuser
# During debugging, this entry point will be overridden. For more information, please refer to https://aka.ms/vscode-docker-python-debug
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "django_blog_app.wsgi"]
docker build --tag django_blog_app:v0 .
docker run -p 8000:8000 --detach --name dba django_blog_app:v0
dba
: name of deployed containerdjango_blog_app:v0
: name of deployed containerlocalhost:8000
to access the deployed containerdocker stop wn
docker ps # lists all running containers
docker exec -it dba /bin/bash # start interactive docker container's internal CLI session
$ django-admin
in zsh lists all django sub-commands
# settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': '<db-name-set-in-sql-cli>'
'USER': 'root' ,
'PASSWORD': 'test-1234' ,
'HOST': 'localhost',
'PORT': '3306'
}
}
brew install mysql # installs brew mysql
mysql -u root -p # login to sql server as root user
brew services start mysql # load brew version of mysql
brew services start mysql # stop brew version of mysql
lsof -i:3306 # list processes using port 3306