Python Django 概念與實作 - Django View(一)

前言

Django採用MVT(model, view, tempalte)架構,這節來大概說一下其大致的流程、MVT與MVC差異,最後介紹view中常用的函數與方法~


目前 Python Django 概念與實作 大致規劃為

  1. 佈置環境
  2. Django View(一)
  3. Django Model(二)
  4. Django Test(三)
  5. Django Forms(四)
  6. Django Admin(五)
  7. 登入功能(六)
  8. Blog功能(七)

MVT

MVT
MVT(model, view, tempalte)框架與MVC(model, view, controller)其實根本一樣(?)。在MVT中,View負責處理邏輯、返回response(接收request由url負責),Template負責頁面渲染,Model負責與DB server存取data。
在MVC中,Controller負責接收request、返回response、與view溝通,View負責處理邏輯、頁面渲染,Model一樣負責存取data。

Create Application

介紹完django中的MVT框架後,我們建立一個view看看。
除了基本設定外,django可以新增各種子功能,這裡我們新增一個posts功能

python manage.py startapp posts

django會自動幫我們新增一個post資料夾,與主設定App(main)同一層

1
2
3
4
5
6
7
8
9
10
11
12
13
14
.
├── .venv
├── .env
├── manage.py
├── db.sqlite3
├── main/
└── posts
├── __init__.py
├── admin.py # 後台設定
├── apps.py
├── migrations/ # 資料庫版本控制
├── models.py # 資料庫table models(模型)
├── tests.py # 測試
└── views.py # 程式邏輯
  1. posts/apps找到app設定檔名PostsConfig後,註冊app
    1
    2
    3
    4
    5
    # posts/apps.py
    from django.apps import AppConfig
    class PostsConfig(AppConfig): # app設定檔名
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'posts'
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    # main/setting.py
    # 略

    # Application definition
    INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'posts.apps.PostsConfig', # 註冊app
    ]
  2. 修改views.py
    1
    2
    3
    4
    5
    6
    # posts/views.py
    from django.shortcuts import render
    from django.http import HttpResponse

    def index(request):
    return HttpResponse('Blog - 首頁')

    Tips
    views.py中,傳至前端必須是一個HttpResponse物件,無法像Flask一樣直接返回字串

  3. 新增urls.py,注意⚠️這裡的URL是接在main_app URL後方。e.g. localhost:8000/blog/index
    1
    2
    3
    4
    5
    6
    7
    8
    # posts/urls.py
    from django.urls import path
    from posts import views

    app_name = 'posts'
    urlpatterns = [
    path("", views.index, name="index"),
    ]
  4. 修改main/urls.py,註冊app URL,這裡才是第一層的URL。e.g. localhost:8000/blog
    1
    2
    3
    4
    5
    6
    7
    8
    # main/urls.py
    from django.contrib import admin
    from django.urls import path, include

    urlpatterns = [
    path('admin/', admin.site.urls),
    path('blog', include('posts.urls')),
    ]

儲存後,在/blog就可以看到我們的第一個網頁🎉

Template

viws.py中,直接注入html是不切實際的,django也支援使用template(模板)

  1. post中新增templates/post資料夾

    Notices
    雖然可以直接將.html丟入templates資料夾中,但django仍然建議我們多一層app_name資料夾,避免django搞混

  2. 新增base.html,這裡我新增了一個bootstrap的navbar
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    <!-- posts/templates/posts/base.html -->
    <!doctype html>
    <html lang="zh-CN">
    <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- Bootstrap CSS -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-F3w7mX95PdgyTmZZMECAngseQB83DfGTowi0iMjiWaeVhAn4FJkqJByhZMI3AhiU" crossorigin="anonymous">

    <title>My blog</title>
    </head>
    <body>
    <header>
    <nav class="navbar navbar-expand-lg navbar-light bg-light">
    <div class="container-fluid">
    <a class="navbar-brand" href="#">My Blog</a>
    <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
    <span class="navbar-toggler-icon"></span>
    </button>
    <div class="collapse navbar-collapse" id="navbarSupportedContent">
    <ul class="navbar-nav me-auto mb-2 mb-lg-0">
    <li class="nav-item">
    <a class="nav-link active" aria-current="page" href="#">Home</a>
    </li>
    <li class="nav-item">
    <a class="nav-link" href="#">Link</a>
    </li>
    <li class="nav-item dropdown">
    <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
    Dropdown
    </a>
    <ul class="dropdown-menu" aria-labelledby="navbarDropdown">
    <li><a class="dropdown-item" href="#">Action</a></li>
    <li><a class="dropdown-item" href="#">Another action</a></li>
    <li><hr class="dropdown-divider"></li>
    <li><a class="dropdown-item" href="#">Something else here</a></li>
    </ul>
    </li>
    </ul>

    </div>
    </div>
    </nav>
    </header>
    <main>
    {% block container %}
    {% endblock %}
    </main>
    <!-- JS files -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-/bQdsTh/da6pkI1MST/rWKFNjaCP5gBSY4sEBT38Q/9RBh9AH40zEOg7Hlq2THRZ" crossorigin="anonymous"></script>
    </body>
    </html>
  3. 新增index.html,並繼承base.html
    1
    2
    3
    4
    <!-- posts/templates/posts/index.html -->
    {% extends 'posts/base.html' %}
    {% block container %}
    {% endblock %}
  4. 修改posts/views.py
    1
    2
    3
    4
    5
    6
    7
    # posts/views.py
    from django.shortcuts import render
    from django.http import HttpResponse
    from django.template import loader
    def index(request):
    template = loader.get_template('posts/index.html')
    return HttpResponse(template.render(request=request))
    存檔後可以看到bootstrap樣式
    index

Context

Context(上下文),僅將模板輸出至前端是不夠的,django使用dict方法提供我們將變數輸出至前段

  1. 修改posts/views.py
    1
    2
    3
    4
    5
    6
    7
    8
    # posts/views.py`
    from django.shortcuts import render
    from django.http import HttpResponse
    from django.template import loader
    def index(request):
    template = loader.get_template('posts/index.html')
    context = {'username': 'MC'}
    return HttpResponse(template.render(context=context, request=request))
  2. 修改posts/templates/posts/index.html
    1
    2
    3
    4
    5
    <!-- posts/templates/posts/index.html -->
    {% extends 'posts/base.html' %}
    {% block container %}
    <h1>Welcome, {{ username }}</h1>
    {% endblock %}

Render

每一次都要使用HttpResponseloader有點麻煩,django預設是使用更為簡潔的render

  1. 修改posts/views.py
    1
    2
    3
    4
    5
    # posts/views.py
    from django.shortcuts import render
    def index(request):
    context = {'username': 'MC'}
    return render(request, 'posts/index.html', context=context)

    Tips
    雖然render()更為簡潔,但本質仍是HttpResponse物件

Static Files

django中的static file(.css, .js, .img)放置方式與template相同

  1. 修改setting.py ❗️重要❗️
    1
    2
    3
    4
    # main/setting.py
    # 略...
    # STATIC_URL = 'static/'
    STATIC_URL = '/static/' # 多加'/'
  2. posts中新增/static/posts
  3. /ststic/posts下新增/img/django.png,可自行抓取圖片
    1
    2
    3
    4
    5
    6
    posts
    └── static
    └── posts
    └── img
    └── django.png

  4. .html引入static就可以使用static
    1
    2
    3
    4
    5
    6
    7
    <!-- posts/index.html -->
    {% extend base.html %}
    {% load static %}
    {% block container %}
    <h1>Welcome, {{ username }}</h1>
    <img src="{% static 'posts/img/django-icon.png' %}" style="height: 200px;">
    {% endblock %}

    Notices
    即使base.html中已經有{% load static %}標籤,子模板中仍需要使用該標籤,否則會顯示TemplateSyntaxError

結論

  • MVT, MVC框架
  • 新增app
  • 修改URL
  • HttpResponse返回至前端
  • loader()渲染html
  • context(上下文)
  • render()快速渲染
  • 使用template, static