# 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 :::