---
# System prepended metadata

title: Django 101
tags: [website, code, Python]

---

# Django 101
###### tags: `code` `Python` `website`

---
## Reference
:::info
- [Main Reference](https://www.learncodewithmike.com/search/label/Django%E6%95%99%E5%AD%B8%E7%B3%BB%E5%88%97?updated-max=2020-04-16T20:54:00%2B08:00&max-results=20&start=6&by-date=false)
- [Installation](https://www.learncodewithmike.com/2020/03/django-install.html)
- [Django 30 days](https://ithelp.ithome.com.tw/articles/10199515)
:::

---
## Environment Setting
![](https://i.imgur.com/UVT93Ug.png)
Django integrates many apps into a website architecture
:::success
- "\_\_init\_\_.py": used to represent the directory is a package
- "asgi.py": asynchronous server gateway interface, provide asynchronization related function
- "settings.py": project configuration
- "urls.py": define the url of each app
- "wsgi.py": web server gateway interface, provide the interface between server and Django website
- "manage.py": manage the whole project, e.g. start up local server, connect with database, establish app, etc
:::
### Installation
```shell=
sudo apt-get install python-pip # install package manager first
pip install django # and you can specify the version like "pip install django==3.0.3"
```
### Running
```shell=
django-admin startproject <project> <directory> # e.g. django-admin startproject jojo_project .
python manage.py runserver
```
> if it deosn't work, try instruction "python3 -m django startproject cheese ."
> "python3 manage.py runserver"
- and then access it on local via browser

---
## Main
:::info
- [architecture](https://www.youtube.com/watch?v=u4XIMTOsxJk)
:::
- Django MTV mode
  ![](https://i.imgur.com/KYRQusF.png)
### App Establishment
:::info
- [reference](https://www.learncodewithmike.com/2020/03/django-create-app.html)
:::
:::success
- "migrations": records the procedure of synchronization between database and models.py
- "admin.py": used to define or customize the indication on Django backstage of the app
- "apps.py": the app configuration
- "models.py": define the app field in the database
- "tests.py": automatic testing script
- "views.py": receives the request from browser, after handling, response back to the browser
:::
```shell=
python3 manage.py startapp <app name> # create the app
```
- set "settings.json" in main folder into the following statement![](https://i.imgur.com/85729s0.png)
- create new "urls.py" on app "posts" directory
```python=
from django.urls import path
from . import views # import the file "views" from the current directory
urlpatterns = [
    path('jojo-test1', views.index, name = "Index")
]
```
- modify "urls.py" in main folder to the following statement
```python=
from django.contrib import admin
from django.urls import path, include # import function "include"
urlpatterns = [
    path('admin/', admin.site.urls),
    path('posts/', include('posts.urls')) # add the url of new app
]
```
- modify the "views.py" in app folder
```python=
from django.shortcuts import render
from django.http.response import HttpResponse
# Create your views here.
def index(request):
    return HttpResponse("jojowei test1")
```
- access the main page![](https://i.imgur.com/4ClAvvQ.png)
- access the new app page![](https://i.imgur.com/5h3E5Xh.png)

### Django Migration
:::info
- [reference](https://www.learncodewithmike.com/2020/03/django-model-migration.html)
:::
Django use ORM (Object Relational Mapping) technology to implement the object concept
- to establish the data of attraction, location (one) to post (many)
- modify the "models.py" in app folder to the following statement
```python=
from django.db import models
from django.utils import timezone

# attraction id
class Location(models.Model):
    name = models.CharField(max_length=255) # the name of attraction
# the post of attraction
class Post(models.Model):
    subject = models.CharField(max_length=255) # title
    content = models.CharField(max_length=255) # content
    author = models.CharField(max_length=20) # poster
    create_date = models.DateField(default=timezone.now) # post time
    location = models.ForeignKey(Location, on_delete=models.CASCADE) # the location of attraction
    # "on_delete=models.CASCADE" means if attraction is deleted, post will be deleted, too
```
- the field class need to inherit class "Model" to execute CRUD operation
    - data type in module "models"
        - CharField
        - IntegerField
        - FloatField
        - DateField
        - ImageField
- import the directory of the app configuration class in "settings.json" of main folder![](https://i.imgur.com/fWOmw2X.png)
```json=
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'posts.apps.PostsConfig'
]
```
- Django ++Migration++, is a file, used to synchronize class in "models.py" and database
- so if we modify "models.py", we have to run the following instruction
```shell=
python3 manage.py makemigrations
```
- we can observe the change on directory "posts/migrations/0001_initial.py"![](https://i.imgur.com/8NgV36S.png)
- after definition, we can synchronize class "models" with the database![](https://i.imgur.com/larjbJj.png)
```shell=
python3 manage.py migrate
```
- if you want to observe the SQL instruction while migration
```shell=
python3 manage.py sqlmigrate <app name> <file code of migration> # e.g. python3 manage.py sqlmigrate posts 0001
```

### SQLite
a light database, used in few number data environment, and we can check the status via [SQLite Browser](https://sqlitebrowser.org/dl/)
```shell=
sudo apt-get install sqlitebrowser
sqlitebrowser # open the app
```
- and you can watch these field![](https://i.imgur.com/jZ7HTXx.png)

### Django Administration
- register a super user first
```shell=
python3 manage.py createsuperuser
# user id: jojowei
# mail: lin.xiao.wei60105@gmail.com
# password: wei
```
- enter the administration through "ip addr/admin"
```shell=
python3 manage.py runserver # run the server first
```
- customize the administration via "admin.py" in app folder![](https://i.imgur.com/T3qzCt2.png)
- customize the field indication
```python=
from django.contrib import admin
from .models import Location, Post

class LocationAdmin(admin.ModelAdmin): # inherited from class "ModelAdmin"
    list_display = ('id', 'name') # method overriding

class PostAdmin(admin.ModelAdmin):
    list_display = ('subject', 'content', 'author', 'location')
    
    # as if we don't want the specific field in form list
    fields = ('subject', 'content', 'author', 'location') # method overriding
    # method 2: "exclude = ('create_date',)"

# register the field to administration
admin.site.register(Location, LocationAdmin) # register inheritance class
admin.site.register(Post, PostAdmin)
```
- modify "models.py" in app folderc to the following statement![](https://i.imgur.com/YOkMhMX.png)
```python=
class Location(models.Model):
    name = models.CharField(max_length=255)
     
    def __str__(self):
        return self.name # magic method overriding
```
### Django Template
:::info
- [reference](https://www.learncodewithmike.com/2020/03/django-template.html)
:::
- open "views.py", and import class "Post" from "models.py"
- select the data we want, and respond template (render()) to "html"
```python=
from django.shortcuts import render
from django.http import HttpResponse
from .models import Post
def index(request):
    posts = Post.objects.all() # select all
    posts_taichung = Post.objects.filter(location="taichung") # select the specific field object
    posts_id1 = Post.objects.get(id=1) # select the specific id object
    
    return render(request, "posts/index.html", {"posts": posts})
```
- build another directory for template to avoid conflict of namespace![](https://i.imgur.com/ikRb4PD.png)
- index.html
  ![](https://i.imgur.com/hTmgZJv.png)
- you can access the form on browser![](https://i.imgur.com/BUaYCv1.png)

### Django Template with Bootstrap
:::info
- [reference](https://www.learncodewithmike.com/2020/03/django-bootstrap.html)
:::
- install package we need
```shell=
pip install requests
```
- [open data from PIXNET API](https://developer.pixnet.pro/#!/doc/pixnetApi/mainpageBlogCategories)
- open "views.py" in app folder
```python=
from django.shortcuts import render
import requests

def index(request):
    response = requests.get("https://emma.pixnet.cc/mainpage/blog/categories/hot/28") # 28 is domestic travel
    articles = response.json()["articles"] # transfer data into json object, and access "article" field
    return render(request, "posts/index.html", {"articles": articles})
```
- define a base template which consists of website name, navbar, etc, and combines them into a page
- create a new directory for base template![](https://i.imgur.com/kWThpTo.png)
- ["base.html" with bootstrap](https://getbootstrap.com/docs/5.1/getting-started/introduction/#starter-template)`, and BootStrap is a popular CSS framework`
```htmlembedded=
<html lang="en">
<head>
    <!-- Required meta tags -->
    <!-- Bootstrap CSS -->
    <link crossorigin="anonymous" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" rel="stylesheet"></link>
    <title>NTUST-cheese</title>
</head>
<body>
    <div class="container">
        {% block content %}
        {% endblock  %}
    </div>
    <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script crossorigin="anonymous" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" src="https://code.jquery.com/jquery-3.4.1.slim.min.js"></script>
    <script crossorigin="anonymous" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js"></script>
    <script crossorigin="anonymous" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"></script>
</body>
</html>
```
- modify "settings.json" in main folder![](https://i.imgur.com/pZTpF4M.png)`remember to import "os"`
```json=
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(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',
            ],
        },
    },
]
```
- "index.html" in template folder from app
    - extends base template, and it only places block code
    - [component --> card](https://getbootstrap.com/docs/4.4/components/card/)
- base on this example
  ![](https://i.imgur.com/pD1YJb2.png)
```javascript=
<div class="card" style="width: 18rem;">
  <img src="..." class="card-img-top" alt="...">
  <div class="card-body">
    <h5 class="card-title">Card title</h5>
    <p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
    <a href="#" class="btn btn-primary">Go somewhere</a>
  </div>
</div>
```
- implements bootstrap code on index.html
```htmlembedded=
{% extends 'base.html' %}

{% block content %}
<div class="row">
    {% for article in articles %}
    <div class="col-lg-4">
        <div class="card" style="width: 18rem;">
            <img alt="..." class="card-img-top" src="{{ article.thumb }}" />
            <div class="card-body">
                <h5 class="card-title">{{ article.user.display_name }}</h5>
                <p class="card-text">{{ article.title }} . . .</p>
                <a class="btn btn-primary" href="{{ article.link }}">content</a>
            </div>
        </div>
    </div>
    {% endfor %}
</div>
{% endblock  %}
```
- specify the size of images
```htmlembedded=
<img alt="..." class="card-img-top" src="{{ article.thumb }}" style="height: 12rem; width: 18rem;" />
```
- specify the text number of article
```htmlembedded=
<div class="card-text">{{ article.title|slice:"0:25" }} . . .</div>
```
- add spacing between every article list
```htmlembedded=
<div class="card mt-4" style="width: 18rem;"></div>
```
- result
```htmlembedded=
{% extends 'base.html' %}

{% block content %}
<div class="row">
    {% for article in articles %}
    <div class="col-lg-4">
        <div class="card mt-4" style="width: 18rem;">
            <img alt="..." class="card-img-top" src="{{ article.thumb }}" style="height: 12rem; width: 18rem;"/>
            <div class="card-body">
                <h5 class="card-title">{{ article.user.display_name }}</h5>
                <p class="card-text">{{ article.title|slice:"0:25" }} . . .</p>
                <a class="btn btn-primary" href="{{ article.link }}">content</a>
            </div>
        </div>
    </div>
    {% endfor %}
</div>
{% endblock  %}
```
### Django ModelForm with CRUD
:::info
- [reference](https://www.learncodewithmike.com/2020/03/django-modelform.html)
- [github](https://github.com/mikeku1116/django-urexpenses)
- [data migration](https://hackmd.io/w7HPnUFiQzqpfARVEH3A9Q?view#Django-Migration)
:::
the theme is bookkeeping website
- open "models.py" in app folder and create data field like this
```python=
from django.db import models

class Expense(models.Model):
    name = models.CharField(max_length=255) # name
    price = models.IntegerField() # cost
```
```shell=
# remember to update
python3 manage.py makemigrations
python3 manage.py migrate
```
- modify "admin.py" in app folder
```python=
from django.contrib import admin
from .models import Expense

class ExpenseAdmin(admin.ModelAdmin): # inherited from class "ModelAdmin"
    list_display = ('name', 'price') # method overriding

# register the field to administration
admin.site.register(Expense, ExpenseAdmin) # register inheritance class
```
- create a file "forms.py" inherited from class ModelForm
```python=
from django import forms
from .models import Expense

class ExpenseModelForm(forms.ModelForm):
    class Meta:
        model = Expense
        # fields = ('name', 'price')
        fields = '__all__' # indicates all field characters
        
        # other characters like widgets (apply CSS class of bootstrap's form properties), labels
        widgets = {
            'name': forms.TextInput(attrs={'class': 'form-control'}),
            'price': forms.NumberInput(attrs={'class': 'form-control'})
        }
        labels = {
            'name': 'item',
            'price': 'cost'
        }
```
- in general, there are two http method, GET and POST, and the default mode is GET
- http method of form with "views.py" in app folder
```python=
from django.shortcuts import render
from .forms import ExpenseModelForm

def index(request):
    form = ExpenseModelForm()
    context = {
        'form': form
    }
    return render(request, 'expenses/index.html', context)
```
- create a new "index.html" in app templates for "forms.py"![](https://i.imgur.com/89YhHTz.png)
    - label "action" in "form" is to send the target URL
    - "csrf_token" is Django security mechanism for form authentication
- version 1![](https://i.imgur.com/8F8Vt9W.png)
```htmlembedded=
{% extends 'base.html' %}

{% block content %}
<form action="" method="POST">
    {% csrf_token %}
    <div class="form-group">
        <div class="form-group col-md-6">
            
            {{ form }}

            <input class="btn btn-success" type="submit" value="submission" />
        </div>
    </div>
</form>
{% endblock  %}
```
- version 2 (more flexible)![](https://i.imgur.com/6Rj8Sxa.png)
```htmlembedded=
{% extends 'base.html' %}

{% block content %}
    <form action="" method="POST">
        {% csrf_token %}
        <div class="form-row">
            <div class="form-group col-md-6">
                <label>{{ form.name.label }}</label>
                {{ form.name }}
            </div>
            <div class="form-group col-md-4">
                <label>{{ form.price.label }}</label>
                {{ form.price }}
            </div>
            <div class="form-group col-md-2">
                <input class="btn btn-success" style="margin-top: 30px;" type="submit" value="submission" />
            </div>
        </div>
    </form>
{% endblock  %}
```
- use Django ModelForm to new data operating
    - POST data to database via "views.py" in app folder
    - to add data indication below the form on html page
```python=
from django.shortcuts import render, redirect
from .models import Expense
from .forms import ExpenseModelForm
def index(request):
    expenses = Expense.objects.all() # query the all datas
    form = ExpenseModelForm()
    if request.method == "POST":
        form = ExpenseModelForm(request.POST)
        if form.is_valid(): # check whether the form is valid or not
            form.save()
        # return redirect("/posts/jojo-test1") # redirect to specific page
    context = {
        'expenses': expenses,
        'form': form
    }
    return render(request, 'expenses/index.html', context)
```
- back to "index.html" in "expenses" template, new a function of all data indication![](https://i.imgur.com/fkcFclZ.png)
```htmlembedded=
{% extends 'base.html' %}
 
{% block content %}
 
    <form action="" method="POST">
        {% csrf_token %}
        <div class="form-row">
            <div class="form-group col-md-6">
                <label>{{ form.name.label }}</label>
                {{ form.name }}
            </div>
            <div class="form-group col-md-4">
                <label>{{ form.price.label }}</label>
                {{ form.price }}
            </div>
            <div class="form-group col-md-2">
                <input type="submit" class="btn btn-success" style="margin-top:30px;" value="submission">
            </div>
        </div>
    </form>
 
    <table class="table table-bordered table-striped">
        <thead class="thead-dark">
            <tr>
                <th>item</th>
                <th>cost</th>
                <th></th>
            </tr>
        </thead>
        <tbody>
            {% for expense in expenses %}
                <tr>
                    <td>{{ expense.name }}</td>
                    <td>{{ expense.price }}</td>
                    <td>
                        <a href="#" class="btn btn-info">modification</a>
                        <a href="#" class="btn btn-danger">deletion</a>
                    </td>
                </tr>
            {% endfor %}
        </tbody>
    </table>
{% endblock  %}
```
- Django URL Reference
    - when we modify the data, we must know the primary key(PK) of data, and transmit PK to "view function" via URL
- modify "urls.py" in app folder
    - define the data type first, and then naming after the colon
```python=
from django.urls import path
from . import views
urlpatterns = [
    path('', views.index, name='Index'),
    path('update/<str:pk>', views.update, name='Update'),
    path('delete/<str:pk>', views.delete, name='Delete')
]
```
- modify "urls.py" in main folder to the following statement
```python=
from django.contrib import admin
from django.urls import path, include # import function "include"
urlpatterns = [
    path('admin/', admin.site.urls),
    path('expenses/', include('posts.urls')) # add the url of new app
]
```
- back to "expenses" template index.html
    - specify the URL name via Django url template syntax, according to urls.py and corresponding to specific view function
    - modify ++the buttons++ to the following statement![](https://i.imgur.com/HvregBx.png)
```htmlembedded=
<a href="{% url 'Update' expense.id %}" class="btn btn-info">modification</a>
<a href="{% url 'Delete' expense.id %}" class="btn btn-danger">deletion</a>
```
- create a new view function "update" and "delete" at "views.py" in app folder
```python=
def update(request, pk):
    expense = Expense.objects.get(id=pk)
    form = ExpenseModelForm(instance=expense) # sepcify the "keyword argument" in Django ModelForm instance to get the form data object to initiate the form
    if request.method == 'POST':
        form = ExpenseModelForm(request.POST, instance=expense)
        if form.is_valid():
            form.save()
        return redirect('/expenses') # refirect the page
    context = {
        'form': form
    }
    return render(request, 'expenses/update.html', context)

def delete(request, pk):
    expense = Expense.objects.get(id=pk)
    if request.method == "POST":
        expense.delete()
        return redirect('/expenses') # back to main page
    context = {
        'expense': expense
    }
    return render(request, 'expenses/delete.html', context)
```
- new a "update.html" in app templates
```htmlembedded=
{% extends 'base.html' %}

{% block content %}

    <form action="" method="POST">
        {% csrf_token %}
        <div class="form-group">
            <div class="form-group col-md-6">

            {{ form }}

            <input class="btn btn-success" type="submit" value="update" />
            </div>
        </div>
    </form>
{% endblock  %}
```
- new a "delete.html" in app templates
```htmlembedded=
{% extends 'base.html' %}

{% block content %}

<h3>ensure that delete {{ expense.name }}?</h3>

<form action="" method="POST">
    {% csrf_token %}
    <input class="btn btn-success" type="submit" value="Yes" />
    <a class="btn btn-warning" href="{% url 'Index' %}">Cancel</a>
</form>

{% endblock  %}
```
- access it at "127.0.0.1:8000/expenses"![](https://i.imgur.com/D5q5O6i.png)

### Django UserCreationForm (for register)
:::info
- [reference](https://www.learncodewithmike.com/2020/04/django-authentication-and-usercreationform.html)
:::
- actually, "UserCreationForm" related models are established defaultly, and we can use it to implement register function![](https://i.imgur.com/59buTx0.png)
- "views.py" in app folder
```python=
from django.shortcuts import render
from django.contrib.auth.forms import UserCreationForm

# main page
def index(request):
    return render(request, 'accounts/index.html')

# register
def sign_up(request):
    form = UserCreationForm()
    context = {
        'form': form
    }
    return render(request, 'accounts/register.html', context)
```
- "index.html" in new templates "account" in app folder
```htmlembedded=
<!-- for test -->
{% extends 'base.html' %}

{% block content %}

<div class="jumbotron mt-3">
    <h1>Django AuthDev HomPage</h1>
    <p class="lead">This example is a quick demo to illustrate how django authentication works.</p>
    <a class="btn btn-lg btn-primary" href="{% url 'Logout' %}" role="button">logout &#187;</a>
</div>

{% endblock  %}
```
- "register.html” in new templates “account” in app folder
```htmlembedded=
{% extends 'base.html' %}

{% block content %}

<!-- main screen -->
<h1>AuthDev</h1>
<div class="jumbotron mt-3">
    <h3>Register</h3>

    <form action="" method="POST">
        {% csrf_token %}
        {{ form }}

        <br/>
        <input class="btn btn-success" type="submit" value="submit" />
    </form>
</div>

{% endblock  %}
```
- modify "urls.py" in main folder to the following statement
```python=
from django.contrib import admin
from django.urls import path, include # import function "include"
urlpatterns = [
    path('admin/', admin.site.urls),
    path('accounts/', include('posts.urls')) # add the url of new app
]
```
- modify "urls.py" in app folder to the following statement
```python=
from django.urls import path
from . import views
urlpatterns = [
    path('', views.index, name='Index'),
    path('register', views.sign_up, name='Register')
]
```
- outcome![](https://i.imgur.com/gw7abA6.png)
    - all of the forms are "Django UserCreationForm" built-in indication
    - there are many verification message, they will dispear when we customize "UserCreationForm"
- customize "Django UserCreationForm"
- "forms.py" in app folder
```python=
from django import forms
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
class RegisterForm(UserCreationForm):
    username = forms.CharField(
        label="Account",
        widget=forms.TextInput(attrs={'class': 'form-control'})
    )
    email = forms.EmailField(
        label="GMail",
        widget=forms.EmailInput(attrs={'class': 'form-control'})
    )
    password1 = forms.CharField(
        label="Password",
        widget=forms.PasswordInput(attrs={'class': 'form-control'})
    )
    password2 = forms.CharField(
        label="Password Assurance",
        widget=forms.PasswordInput(attrs={'class': 'form-control'})
    )
    class Meta:
        model = User
        fields = ('username', 'email', 'password1', 'password2')
```
- "views.py" in app folder
```python=
from django.shortcuts import render, redirect
from django.contrib.auth.forms import UserCreationForm
from .forms import RegisterForm

# main page
def index(request):
    return render(request, 'accounts/index.html')

# register
def sign_up(request):
    form = RegisterForm()
    if request.method == "POST":
        form = RegisterForm(request.POST)
        if form.is_valid():
            form.save()
            redirect('/accounts/') # redirect page
    context = {
        'form': form
    }
    return render(request, 'accounts/register.html', context)
```
- outcome
  ![](https://i.imgur.com/BKMD3S0.png)
- use "Django Form" to implement login function
    - comparison
        - ModelForm needs data model
        - Form is regular form, don't need to create data model
- "forms.py" in app folder
```python=
from django import forms
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User

class RegisterForm(UserCreationForm):
    username = forms.CharField(
        label="Account",
        widget=forms.TextInput(attrs={'class': 'form-control'})
    )
    email = forms.EmailField(
        label="GMail",
        widget=forms.EmailInput(attrs={'class': 'form-control'})
    )
    password1 = forms.CharField(
        label="Password",
        widget=forms.PasswordInput(attrs={'class': 'form-control'})
    )
    password2 = forms.CharField(
        label="Password Assurance",
        widget=forms.PasswordInput(attrs={'class': 'form-control'})
    )
    class Meta:
        model = User
        fields = ('username', 'email', 'password1', 'password2')

class LoginForm(forms.Form):
    username = forms.CharField(
        label="Account",
        widget=forms.TextInput(attrs={'class': 'form-control'})
    )
    password = forms.CharField(
        label="Password",
        widget=forms.PasswordInput(attrs={'class': 'form-control'})
    )
```
- "views.py" in app folder
    - if we want to implement "login_required", we must [clear the cookie first](https://stackoverflow.com/questions/26698154/django-login-required-not-redirecting)
```python=
from django.shortcuts import render, redirect
from django.contrib.auth.forms import UserCreationForm
from .forms import RegisterForm
from .forms import LoginForm
# for authentication
from django.contrib.auth import authenticate, login, logout 
from django.contrib.auth.decorators import login_required

# main page with authentication
@login_required(login_url="Login")
def index(request):
    return render(request, 'accounts/index.html')

# register
def sign_up(request):
    form = RegisterForm()
    if request.method == "POST":
        form = RegisterForm(request.POST)
        if form.is_valid():
            form.save()
            return redirect('/accounts/login') # redirect page
    context = {
        'form': form
    }
    return render(request, 'accounts/register.html', context)

# log in
def sign_in(request):
    form = LoginForm()
    
    # user authentication
    if request.method == "POST":
        username = request.POST.get("username")
        password = request.POST.get("password")
        user = authenticate(request, username=username, password=password)
        if user is not None:
            login(request, user)
            return redirect('/accounts') # redirect to main page
    context = {
        'form': form
    }
    return render(request, 'accounts/login.html', context)

# log out
def log_out(request):
    logout(request)
    return redirect('/accounts/login') # redirect to login page
```
- "login.html" in app template
```python=
{% extends 'base.html' %}

{% block content %}

<h1>AuthDev</h1>
<h3>Login</h3>

<div class="jumbotron bg-secondary text-white">
    <form action="" method="POST">
        {% csrf_token %}
        {{ form }}
        <br>
        <input class="btn btn-dark btn-block" type="submit" value="login" />
        <p>Do not have an account yet? <a href="{% url 'Register' %}" style="color:white;">Register</a></p>
    </form>
</div>

{% endblock  %}
```
- "urls.py" in app folder
```python=
from django.urls import path
from . import views

urlpatterns = [
    path('', views.index, name='Index'),
    path('register', views.sign_up, name='Register'),
    path('login', views.sign_in, name='Login'),
    path('logout', views.log_out, name='Logout')
]
```

---
## Django on Heroku
:::info
- [reference](https://www.learncodewithmike.com/2020/04/django-heroku.html)
- [debug 1](https://www.zoearthmoon.net/blog/program/item/2592.html)
- [debug 2](https://segmentfault.com/q/1010000010016708)
- [fast deployment](https://blog.typeart.cc/%E4%BD%88%E7%BD%B2django%E5%88%B0heroku/)
- [official document](https://devcenter.heroku.com/articles/django-app-configuration)
:::
- [sign up first](https://dashboard.heroku.com/)
- [install Heroku CLI](https://devcenter.heroku.com/articles/heroku-cli)
- install git tool
- install "gunicorn" package
```shell=
pip install gunicorn
```
- create file "runtime.txt"
```shell=
python-3.8.10
```
- create file "requirements.txt" to place the required package
```python=
django_heroku
django
gunicorn
whitenoise
dj_database_url
requests
```
- or use this command to solve
```shell=
pip freeze > requirements.txt
```
- create file "Procfile" in project directory![](https://i.imgur.com/9X6VPzp.png)
- like this statement![](https://i.imgur.com/9ozzoxo.png)

```shell=
web: gunicorn <project name>.wsgi
```
- there are some static file like "javascript", "CSS", images needed to be deployed, so we need to create a "static" folder to place the static file
- create "static" folder![](https://i.imgur.com/HKGQGtE.png)
- new the following statement in "settings.py" in main folder
```python=
import django_heroku # the top of the file

BASE_DIR = os.path.dirname(os.path.abspath(__file__))
STATIC_ROOT = os.path.join(BASE_DIR, 'static')

django_heroku.settings(locals()) # the bottom of the file
```
- run this instruction to collect static file
```python=
python manage.py collectstatic
```
- install "whitenoise" to deploy static file
```shell=
pip install whitenoise
```
- add "WhiteNoiseMiddleware" statement into "settings.py" in main folder and folloew the statement below![](https://i.imgur.com/g7mOi8x.png)
```python=
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'whitenoise.middleware.WhiteNoiseMiddleware',
    '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',
]
```
- and then start deploying via Heroku CLI
```shell=
heroku login # connect with remote
heroku create <app_name> # create the app on heroku
```
- add the URL configuration to "settings.py" in main folder![](https://i.imgur.com/YGZvd3f.png)
```python=
ALLOWED_HOSTS = [
    'ntust-cheese-login.herokuapp.com/'
]
```
- go to main directory
```shell=
git init
git add . # if there is any modification, just implement from this step
git commit -m "message"
heroku buildpacks:set heroku/python # select default language
heroku git:remote -a <app_name> # switch Heroku Git Repo
git push heroku master # push to Heroku Git Repo
heroku ps:scale web=1 # set the number of host
heroku open # open the website
heroku logs --tail # check out the logs
```
- [finally, remember to add super user and migrate](https://www.itread01.com/content/1546240897.html)
```shell=
heroku run python manage.py makemigrations
heroku run python manage.py migrate
heroku run python manage.py createsuperuser
# user: jojowei
# mail: lin.xiao.wei60105@gmail.com
# password: wei
```

---
## Issue
:::danger

:::
