Lecturer:Yan
!! 本課程只是入門,並不會深入解釋所有概念 !!
有興趣的話可以加入 SIRLA 或是繼續自學 ouo
網頁框架就是別人已經設定好的一個網站模板,你可以學習它的規則,然後修改成自己需要的樣子。簡單來說就是當你開發 Web 應用程式時所用的框架。
通常會提供:
常見的網頁框架
框架本身無法自行運作,開發者要將框架搭配上自己的程式,才是一個完整的應用程式。
它有以下幾個特色:
Don't Repeat Yourself
Text
負責資料庫
要呈現哪一個資料,
並交由 Template 呈現
最後的 HTML 呈現
使用 MTV 架構的好處:
建立好後,會出現剛剛取名的資料夾,切換進去
$ cd mysite
// 如果你剛剛取不一樣的名字
$ cd 名字
$ 不用打,只要打後面的指令
$ cd 到你想放資料夾的地方
$ mkdir 隨便你取
建好你要當作虛擬環境的資料夾
$ cd 到資料夾
前往該資料夾
$ python -m venv 你想取的名字
// 舉例來說
$ python -m venv venv
創建虛擬環境
$ pip install virtualenv
如果你是用 mac 記得先安裝
$ 你剛剛取的虛擬環境名稱\Scripts\activate
$ source 你剛剛取的虛擬環境名稱/bin/activate
$ pip install django
安裝 Django
$ pip list
可以先 exit 到虛擬環境外後,再輸入:
這時候你就會發現剛剛在虛擬環境裡安裝的套件,真的沒有在外面。
首先,使用 django-admin.py 來建立第一個 Django project mysite :
$ django-admin.py startproject mysite
// 這裡是以取名為 mysite 為例
$ django-admin.py startproject 這裡可以放你要的名字
runserver 會啟動一個簡單的 web server,方便於在開發階段使用
$ cd mysite // 先進到剛剛建好的 project 資料夾
$ python manage.py runserver
利用 startapp 建立一個 Django app - blog
$ python manage.py startapp blog
// 這裡是以取名為 blog 為例
$ python manage.py startapp 這裡可以放你要的名字
打開 mysite/settings.py,找到 INSTALLED_APPS
# 檔案位置:mysite/settings.py
...
# Application definition
INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'blog', <--- !! 加在這裡 !!
)
Django view 其實是一個 function,處理 HttpRequest 物件,並回傳 HttpResponse 物件。
一個網頁程式當中的邏輯通常是這個樣子的:
在 Django 當中,處理這部分的邏輯稱之為 Django Views,通常我們都會放在 Django APP 當中的 views.py 檔案裡面。
request
進來
從資料庫
撈資料
處理
資料
把網頁
呈現出來
建立一個名為 hello_world 的 view
在 blog/views.py 輸入下列程式碼:
# 檔案位置:mysite/blog/views.py
from django.http import HttpResponse
def hello_world(request):
return HttpResponse("Hello World!")
Django 需要知道 URL 與 view 的對應關係
例如有人瀏覽 http://127.0.0.1:8000/hello/ 時 ,hello_world() 這個 view function 需要被執行。
而這個對應關係就是 URL conf (URL configuration)。
現在來設定 Hello World 的 URL conf。
打開 mysite/urls.py,import 剛剛寫的 view function:
from blog.views import hello_world
在 urlpatterns 中加入下面這行:
url(r'^hello/$', hello_world),
urls.py 目前的長相
# 檔案位置:mysite/mysite/urls.py
from django.conf.urls import include, url
from django.contrib import admin
from blog.views import hello_world
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^hello/$', hello_world),
]
url(regex, view)
像剛剛寫得 url(r'^hello/$', hello_world),
現在啟動你的 web server,就像剛剛那樣:
$ cd ../ // 回到 mysite 資料夾
$ python manage.py runserver
在瀏覽器輸入 http://127.0.0.1:8000/hello/,你會看到網頁顯示我們在 HttpResponse 傳入的文字Hello World!
確認目前所在位置是在 mysite 裡
新增一個名為 templates 的資料夾
$ mkdir templates
修改 mysite/settings.py 中的 TEMPLATES 設定
將 'DIRS' 原本的 [] 修改成:
[os.path.join(BASE_DIR, 'templates').replace('\\', '/')]
並在前面引入 os
import os
修改完的 TEMPLATES 設定應該要長這樣
# 檔案位置:mysite/mysite/settings.py
import os #前面要加入這句
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates').replace('\\', '/')],
'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',
],
},
},
]
在 templates 資料夾中新增一個 hello_world.html
<!-- 檔案位置:mysite/templates/hello_world.html -->
<!DOCTYPE html>
<html>
<head>
<title>I come from template!!</title>
<style>
body {
background-color: 你想要的顏色;
}
em {
color: 你想要的顏色;
}
</style>
</head>
<body>
<h1>Hello World!</h1>
<em>{{ current_time }}</em>
</body>
</html>
hello_world.html 的完整長相
<!-- 檔案位置:mysite/templates/hello_world.html -->
<!DOCTYPE html>
<html>
<head>
<title>I come from template!!</title>
<style>
body {
background-color: black;
}
h1 {
color: white;
}
em {
color: white;
}
</style>
</head>
<body>
<h1>Hello World!</h1>
<em>{{ current_time }}</em>
</body>
</html>
掏出(?) 剛剛改過的 blog/views.py
把 hello_world 修改一下
# 檔案位置:mysite/blog/views.py
from datetime import datetime
from django.shortcuts import render
def hello_world(request):
return render(request, 'hello_world.html', {
'current_time': str(datetime.now()),
})
接著再打開來看看
$ python manage.py runserver
在瀏覽器輸入 http://127.0.0.1:8000/hello/
此時可以看到剛剛的模板被套用了,
而字跟背景會是你設定的顏色
將 view function hello_world 修改如下:
# 檔案位置:mysite/blog/views.py
from datetime import datetime
from django.shortcuts import render
def hello_world(request):
return render(request, 'hello_world.html', {
'current_time': str(datetime.now()),
})
# 檔案位置:mysite/mysite/settings.py
...
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
settings.py 中設定了資料庫連線的預設值
這裡為了方便直接使用預設的 SQLite 3
Object Oriented Programming (物件導向程式設計)
將功能抽象化後,用程式表達出來的一種程式設計方式
設計圖
實例化
實際的東西
都可以當成 Object
class ClassName:
你設定的 arrtibute 跟 method
class 的組成包含了
屬性分為兩種:
class motorcycle:
# class attribute
wheels = 2
# instance attributes
def __init__(self, color):
self.color = color
# method 1
def showDescription(self):
print("This motorcycle is ", self.color)
# method 2
def changeColor(self, color):
self.color = color
簡單來說就是在 class 中加入函式
第一個參數一定要是 self,即使 method 沒用到它
def 函式名稱(參數):
程式碼
return 某個值
一個函式的基本架構如下 :
def hello():
print('Hello, World!')
建立一個最基本的函式
def hello():
print('Hello, World!')
hello() # 這樣就是呼叫
呼叫這個函式
def hello(name):
print('Hello,', name)
建立一個 帶參數 的函式
def hello(name):
print('Hello,', name)
hello('yan') # 呼叫時要給參數值
呼叫這個函式
def sum(a, b):
return a + b
建立一個帶有 return 的函式
def sum(a, b):
return a + b
x = sum(3, 4) # 呼叫時要給參數值
print(x)
呼叫這個函式
class Human:
type_name = 'human'
def __init__(self, name, age):
self.name = name
self.age = age
def eat(self):
print("I'm eating.")
Class Human
Attribute |
---|
type name |
name |
age |
Method |
---|
eat |
來寫個叫 Human 的 class 吧
class Human:
type_name = 'human'
def __init__(self, name, age):
self.name = name
self.age = age
class Human:
延續剛剛的程式碼,建立一個叫 yan 的 object :
# 變數名稱 = 函式名稱(參數)
yan = Human('yan', 20)
現在測試一下這個 object
print(yan.name, yan.age)
yan.eat()
class Human 完整程式碼
class Human:
type_name = 'human'
def __init__(self, name, age):
self.name = name
self.age = age
def eat(self):
print("I'm eating.")
yan = Human('yan', 20)
print(yan.name, yan.age)
yan.eat()
如果你還是搞不懂 class,可以參考一下這篇https://www.learnbyexample.org/python-classes-and-objects/
或是現在叫我再講一次
舉個例子
繼承
Attributes
Attributes
Methods
Methods
Human
🇹🇼 Taiwanese
實例化
Attributes
Methods
小明
class Human:
type_name = 'human'
def __init__(self, name, age):
self.name = name
self.age = age
def eat(self):
print("I'm eating.")
class Taiwanese(Human):
def __init__(self, name, age):
super().__init__(name,age)
self.language = 'Chinese'
def drink(self, typeofdrink):
print("I drink %s"%typeofdrink)
先建立父類別(Human)
class Human:
type_name = 'human'
def __init__(self, name, age):
self.name = name
self.age = age
def eat(self):
print("I'm eating.")
class Human:
type_name = 'human'
def __init__(self, name, age):
self.name = name
self.age = age
def eat(self):
print("I'm eating.")
class Taiwanese(Human):
再建立子類別(Taiwanese)
繼承
可以另外增加屬於子類別的 attribute 或 method
class Human:
type_name = 'human'
def __init__(self, name, age):
self.name = name
self.age = age
def eat(self):
print("I'm eating.")
class Taiwanese(Human):
def __init__(self, name, age):
super().__init__(name,age)
yan = Taiwanese('yan',20)
print(yan.name(), yan.language())
yan.eat()
yan.drink('bubble tea')
建立 object ,測試一下剛剛寫的繼承
class Human:
type_name = 'human'
def __init__(self, name, age):
self.name = name
self.age = age
def eat(self):
print("I'm eating.")
class Taiwanese(Human):
def __init__(self, name, age):
super().__init__(name,age)
self.language = 'Chinese'
def drink(self, typeofdrink):
print("I drink %s"%typeofdrink)
yan = Taiwanese('yan',20)
print(yan.name, yan.language)
yan.eat()
yan.drink('bubble tea')
繼承的完整程式碼
# 檔案位置:mysite/blog/models.py
from django.conf import settings
from django.db import models
from django.utils import timezone
class Post(models.Model):
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
title = models.CharField(max_length=200)
text = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
published_date = models.DateTimeField(blank=True, null=True)
def publish(self):
self.published_date = timezone.now()
self.save()
def __str__(self):
return self.title
打開 blog/models.py,刪掉所有東西然後改成:
from django.conf import settings
from django.db import models
from django.utils import timezone
首先 import 一些會用到的套件
而一篇文章中,應該會有以下這些元素
class Post(models.Model):
因為這個 class 是繼承 django.db.models.Model
所以後面小括號要塞進 models.Model
def publish(self):
在 class 中建立一個叫 publish 的 method
將 publish date 指派為當前的時間、儲存
self.published_date = timezone.now()
self.save()
def __str__(self):
return self.title
定義這個 object 的字串描述
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
外來鍵
代表對應到哪個 class
當對應的類別被刪除之後
這些對應到別人的資料要怎麼被處理
id | name |
---|
093 | Erica |
237 | Julie |
521 | Erica |
id | name | class |
---|
093 | Erica | 1 |
237 | Julie | 2 |
521 | Erica | 3 |
1 | A班 |
2 | B班 |
3 | C班 |
class_id | name |
---|
CharField:字串欄位
title = models.CharField(max_length=200)
設定可包含之字元個數
text = models.TextField()
TextField:沒有長度限制的長文本
非必填欄位
created_date = models.DateTimeField(default=timezone.now)
published_date = models.DateTimeField(blank=True, null=True)
DateTimeField:日期與時間欄位
可為空
$ cd 到 mysite
$ python manage.py makemigrations blog
因為我們在 models.py 做了更動,
所以要告訴 Django 我們修改了一些內容
blog 是你的 app 名稱
$ Migrations for 'blog':
blog/migrations/0001_initial.py
- Create model Post
完成後會出現以下的文字敘述
$ python manage.py migrate blog
接著將 Post 這個 model 放進資料庫
blog 是你的 app 名稱
$ Operations to perform:
Apply all migrations: admin, auth, blog...
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
...
完成後會出現類似的文字敘述
首先我們要來同步 Django 專案下 models.py 中
預設的類別(Class) 及資料庫
$ python manage.py migrate
$ python manage.py createsuperuser
而要使用 Django 的管理後台,
我們需要一個管理員帳號。
接著就照著指示建立好帳號,創建密碼時是不會顯示的,要自己記得打到哪裡
# 檔案位置:mysite/blog/admin.py
from django.contrib import admin
from .models import Post
admin.site.register(Post)
還要讓 Django 知道,有哪些 Model 需要管理後台。
修改 blog/admin.py
$ python manage.py runserver
完成後連至 http://127.0.0.1:8000/admin,
即可看到管理後台的登入頁面:
登入後畫面
如果剛剛有註冊好你需要 Django 管理後台的 Model,這邊就會出現它
按下 add posts,就能看到我們剛剛寫的那些欄位
新增一篇文章試試看
# ...
from blog.models import Post
def home(request):
post_list = Post.objects.all()
return render(request, 'home.html', {
'post_list': post_list,
})
在 blog/views.py 內新增
# 檔案位置:mysite/blog/views.py
from datetime import datetime
from django.shortcuts import render
from blog.models import Post
def home(request):
post_list = Post.objects.all()
return render(request, 'home.html', {
'post_list': post_list,
})
def hello_world(request):
return render(request, 'hello_world.html', {
'current_time': str(datetime.now()),
})
目前完整的 blog/views.py
# 檔案位置:mysite/mysite/urls.py
from django.conf.urls import include, url
from django.contrib import admin
from diary.views import hello_world, home # 這邊要新增
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^hello/$', hello_world),
# 還有這裡也要新增
url(r'^$', home),
]
將 mysite/mysite/urls.py 修改
<!-- 檔案位置:mysite/templates/home.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>My blog</title>
</head>
<body>
<h1>Hello! This is my blog</h1>
<div class="container">
{% for post in post_list %}
<div class="post">
<h2><a href="{% url 'post_detail' pk=post.pk %}">{{ post.title }}</a></h2>
<p>{{ post.created_date }}</p>
<p>{{ post.text }}</p>
</div>
{% endfor %}
</div>
</body>
</html>
在 templates 資料夾中新增一個 home.html
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',
],
},
},
確定一下 mysite/mysite/settings.py 是否長這樣
接著再打開來看看
$ python manage.py runserver
現在一打開你就能看到剛剛新增好的首頁啦
from django.shortcuts import render
from django.http import HttpResponse
from blog.models import Post
def hello_world(request):
return HttpResponse("Hello World!")
def home(request):
post_list = Post.objects.all()
return render(request, 'home.html', {
'post_list': post_list,
})
# 新增這邊
def post_detail(request, pk):
post = Post.objects.get(pk=pk)
return render(request, 'post.html', {'post':post})
在 mysite/blog/views.py 內新增
from django.conf.urls import include, url
from django.contrib import admin
from blog.views import hello_world, home, post_detail #新增這裡
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^hello/$', hello_world),
url(r'^$', home),
# 還有這裡也要新增
url(r'^post/(?P<pk>\d+)/$', post_detail, name='post_detail'),
]
修改 mysite/mysite/urls.py
在 blog 資料夾內建立一個 static 資料夾
裡面再建立一個 css 資料夾
venv
mysite/
├── manage.py
├── mysite
├── templates
└── blog
├── static
└── css
h1 a {
color: orange;
}
h1{
text-align: center;
color: #8CB2D0;
}
h2{
text-align: center;
color: #8CB2D0;
font-style:italic;
}
body p {
text-align: center;
font-style:italic;
}
.post {
color: #A89797;
font-family: 'Dancing Script', cursive;
}
.page-header {
background-color: #B6533E;
margin-top: 0;
padding: 20px 20px 20px 40px;
}
.container{
display: flex;
justify-content: space-around;
}
建立 mysite/blog/static/css/blog.css
{% load static %}
<html>
<head>
<meta charset="utf-8">
{% block title %}
{% endblock %}
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css">
<link rel="stylesheet" href="{% static 'css/blog.css' %}">
<link href="https://fonts.googleapis.com/css2?family=Dancing+Script:wght@500&display=swap" rel="stylesheet">
</head>
<body>
<div class="page-header">
<h1><a href="/">My Blog</a></h1>
</div>
{% block content %}
{% endblock %}
</body>
</html>
mysite/templates/base.html
{% extends "base.html" %}
{% block title %}
<title>test blog</title>
{% endblock %}
{% block content %}
<h2>Hello! this is my blog</h2>
<div class="container">
{% for post in post_list %}
<div class="post">
<h2><a href="{% url 'post_detail' pk=post.pk %}">{{ post.title }}</a></h2>
<p>{{ post.created_date }}</p>
<p>{{ post.text|linebreaksbr|slice:":30" }}</p>
</div>
{% empty %}
<p>敬請期待</p>
{% endfor %}
</div>
{% endblock %}
mysite/templates/home.html
{% extends "base.html" %}
{% block title %}
<title>{{ post.title }} | test blog</title>
{% endblock %}
{% block content %}
<div class="post">
<h2><a href="#">{{ post.title }}</a></h2>
<p>{{ post.created_date }}</p>
<p>{{ post.text }}</p>
</div>
{% endblock %}
mysite/templates/post.html
連至 http://127.0.0.1:8000/admin,從管理後台新增一篇測試用文章
$ python manage.py runserver
接著打開首頁 http://127.0.0.1:8000/ 測試看看
應該會看到剛剛新增好的文章,也能點擊進去
{% load static %}
<html>
<head>
<meta charset="utf-8">
{% block title %}
{% endblock %}
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css">
<link rel="stylesheet" href="{% static 'css/blog.css' %}">
<link href="https://fonts.googleapis.com/css2?family=Dancing+Script:wght@500&display=swap" rel="stylesheet">
</head>
<body>
<nav class="navbar">
<a class="navbar-brand" href="/">My blog</a>
<a class="navbar-brand" href="https://github.com/">My github</a>
<a class="navbar-brand" href="/admin">Sign in</a>
</nav>
<div class="page-header">
<h1><a href="/">My Blog</a></h1>
</div>
{% block content %}
{% endblock %}
</body>
</html>
mysite/templates/base.html
/*新增這邊*/
.navbar {
list-style-type: none;
margin: 0;
padding: 0;
background-color: #FCA205;
}
.navbar a{
color: #FFFFFF;
}
/*到這邊*/
h1 a {
color: #FCA205;
}
h1{
text-align: center;
color: #8CB2D0;
}
h2{
text-align: center;
color: #8CB2D0;
font-style:italic;
}
body p {
text-align: center;
font-style:italic;
}
.post {
color: #A89797;
font-family: 'Dancing Script', cursive;
}
.page-header {
background-color: #B6533E;
margin-top: 0;
padding: 20px 20px 20px 40px;
}
.container{
display: flex;
justify-content: space-around;
}
修改 mysite/blog/static/css/blog.css
你的首頁 http://127.0.0.1:8000/ 就會有 navbar 啦
而上面的連結能點擊進去
$ pip install dj-database-url gunicorn dj-static
$ pip install heroku psycopg2-binary
接著將套件全部條列出來
$ pip freeze > requirements.txt
並在 requirements.txt 中加入 psycopg2==2.8.5
大概會長得像下面這樣
asgiref==3.3.1
certifi==2020.12.5
chardet==4.0.0
dj-database-url==0.5.0
dj-static==0.0.6
Django==3.1.7
gunicorn==20.0.4
heroku==0.1.4
idna==2.10
psycopg2-binary==2.8.6
python-dateutil==1.5
pytz==2021.1
requests==2.25.1
sqlparse==0.4.1
static3==0.7.0
urllib3==1.26.4
psycopg2==2.8.5
在 mysite 中(第一層)新增叫做 Procfile 的檔案
web: gunicorn --pythonpath mysite mysite.wsgi
mysite
├── Procfile
├── blog
├── db.sqlite3
├── manage.py
├── mysite
├── requirements.txt
└── templates
在 mysite/mysite 中(第二層)新增叫做 production_settings.py 的檔案
# 檔案位置:mysite/mysite/production_settings.py
# Import all default settings.
from .settings import *
import dj_database_url
DATABASES = {
'default': dj_database_url.config(),
}
# Static asset configuration.
STATIC_ROOT = 'staticfiles'
# Honor the 'X-Forwarded-Proto' header for request.is_secure().
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
# Allow all host headers.
ALLOWED_HOSTS = ['*']
# Turn off DEBUG mode.
DEBUG = False
修改 wsgi.py
# 檔案位置:mysite/mysite/wsgi.py
import os
from django.core.wsgi import get_wsgi_application
from dj_static import Cling
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")
application = Cling(get_wsgi_application())
新增 .gitignore
# 檔案位置:mysite/.gitignore
venv #這裡是你的虛擬環境名稱
*.pyc
__pycache__
staticfiles
db.sqlite3
目前的檔案結構
mysite
├──mysite
│ ├── blog
│ │ ├── __init__.py
│ │ ├── production_settings.py
│ │ ├── settings.py
│ │ ├── urls.py
│ │ └── wsgi.py
│ ├── templates
│ └── manage.py
├── venv
├── .gitignore
├── Procfile
└── requirements.txt
首先到官網註冊一下
$ git init
$ git add .
$ git commit -m "add all"
-m 後面的訊息可自訂
$ set PATH=%PATH%;C:\Program Files\Heroku\bin #Heroku安裝的路徑
$ heroku login
$ heroku create
如果你想要自己取 app 的名稱,指令如下:
$ heroku create 這裡放app名稱
$ heroku config:set DJANGO_SETTINGS_MODULE=mysite.production_settings
$ heroku config:set DJANGO_SETTINGS_MODULE=mysite.production_settings
$ git push heroku master
$ heroku ps:scale web=1
$ heroku run python mysite/manage.py migrate
$ heroku run python mysite/manage.py createsuperuser
$ heroku open