天天看點

Django學習筆記Django Quickstart

Django Quickstart

環境配置

Django環境

pip install pipenv
pipenv shell
pipenv install django
pipenv install djangorestframework

django-admin startproject siteconfig .
python manage.py startapp student
           

以上完成初始化配置,建立了項目和App,此時項目的目錄結構如下。

# tree檢視目錄結構
.
├── manage.py
├── Pipfile
├── Pipfile.lock
├── README.md
├── siteconfig
│   ├── asgi.py
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
└── student
    ├── admin.py
    ├── apps.py
    ├── __init__.py
    ├── migrations
    │   └── __init__.py
    ├── models.py
    ├── tests.py
    └── views.py
           

資料庫環境

Django預設使用sqlite3資料庫,但實際工程環境一般使用mysql或者postgresql等。

(待續)

安裝完成mysql後,還需要在項目檔案做如下配置,以使用MySQL資料庫為例:

  • 安裝所需的包,需要安裝

    pymysql

    cryptography

  • 修改

    settings.py

    的資料庫配置
with open(os.path.join(BASE_DIR, 'siteconfig/MYSQL_PASSWORD.env')) as f:
    MYSQL_PASSWORD = f.read()
DATABASES = {
    'default': {
        # 'ENGINE': 'django.db.backends.sqlite3',
        # 'NAME': BASE_DIR / 'db.sqlite3',
        'ENGINE': 'django.db.backends.mysql',
        'HOST': 'localhost',
        'PORT': '13306',
        'NAME': 'learndjango',
        'USER': 'user',
        'PASSWORD': MYSQL_PASSWORD,
        'TEST': {
            'NAME': 'testdjango',
        }
    }
           
  • 修改應用的

    __init__.py

    檔案
import pymysql

pymysql.install_as_MySQLdb()
           
  • 執行django腳手架指令
python manage.py makemigrations
python manage.py migrate
           

編寫代碼

Django的官方文檔入門教程還是很全面的,講解了後端和前端的基本代碼,最基本的背景一般包括實作資料庫ORM的Model(models.py),以及管理背景admin(admin.py),最基本的前台,包括網站的兩級路由配置(urls.py),視圖層實作業務邏輯(views.py),以及模闆層提供html頁面(templates/*.html)。

後端開發

Django的後端包括Model層和admin管理背景。

ORM層(models.py)

Django的Model API包括字段選項和字段類型兩個部分,詳情見Model官方文檔。

from django.db import models

class Student(models.Model):
    SEX_ITEMS = [
        (1, '男'),
        (2, '女'),
        (0, '未知')
    ]

    STATUS_ITEMS = [
        (0, '申請'),
        (1, '通過'),
        (2, '拒絕')
    ]

    name = models.CharField(max_length=128, verbose_name='姓名')
    sex = models.IntegerField(choices=SEX_ITEMS, verbose_name='性别')
    profession = models.CharField(max_length=128, verbose_name='職業')
    email = models.EmailField(verbose_name='郵箱')
    qq = models.CharField(max_length=128, verbose_name='QQ')
    phone = models.CharField(max_length=128, verbose_name='手機')
    status = models.IntegerField(choices=STATUS_ITEMS, default=0, verbose_name='稽核狀态')
    created_time = models.DateTimeField(auto_now_add=True, editable=False, verbose_name="建立時間")

    def __str__(self) -> str:
        return f'Student: {self.name}'

    class Meta:
        verbose_name = "學員資訊"
        verbose_name_plural = verbose_name
           

管理背景(admin.py)

為快速開發,一般會為每個模型注冊一個管理背景,以便直接操作資料庫,具體可以參考下ModelAdmin options。

  • 背景清單頁的url為/admin/model/model
  • 背景編輯頁的url為/admin/model/model/add或者/admin/model/model/id/change

比較常見的管理背景選項有:

ModelAdmin options 定義
list_display 背景清單頁上顯示的字段,預設為__str__
list_filter 背景清單頁上顯示的篩選字段
search_fields 背景清單頁上用于搜尋的字段
fieldsets 背景編輯頁上顯示的字段
from django.contrib import admin
from .models import Student

class StudentAdmin(admin.ModelAdmin):
    list_display = ('id', 'name', 'sex', 'profession', 'email', 'qq', 'phone', 'status', 'created_time')
    list_filter = ('sex', 'status', 'created_time')
    search_fields = ('name', 'profession')
    fieldsets = (
        (None, {
            'fields': (
                'name',
                ('sex', 'profession'),
            ),
        }),
        ('進階', {
            'classes': ('collapse',),
            'fields': (
                ('email', 'qq', 'phone'),
                'status',
            ),
        }),
    )

admin.site.register(Student, StudentAdmin)
           

前台開發

Django的前台展示資料主要是靠Template層渲染View層,使用DRF時沒有Template層,而送出資料使用Form層。

網站路由(urls.py)

Django的路由是串聯的兩級,第一級是站點檔案夾内的urls.py,第二級是各個APP檔案夾内的urls.py,兩級是url要拼接在一起,并且從上往下比對,可以最下面會放一個404的路由,可參考官方文檔的例子。

from django.contrib import admin
from django.urls import path
from django.urls.conf import include
from .views import index, Index

urlpatterns = [
    path('', index, name='index'),
    path('index', Index.as_view(), name='student_index'),
]
           

業務邏輯層(views.py)

網站路由把通路的url與處理的業務邏輯挂鈎,可以基于函數,也可以基于類。業務邏輯層的輸入是request,函數或類執行個體根據業務邏輯進行加工,并輸出response。在DRF的前後端分離模式下,response是一個json或xml,而在單體應用下,response是一個html。

from django import forms
from django.http.response import HttpResponse, HttpResponseRedirect
from django.shortcuts import render
from django.urls import reverse
from django.views import View
from .models import Student
from .forms import StudentForm

# 基于函數的模闆
def index(request):
    students = Student.objects.all()
    if request.method == 'POST':
        form = StudentForm(request.POST)
        if form.is_valid():
            form.save()
            # cleaned_data = form.cleaned_data
            # student = Student()
            # student.name = cleaned_data['name']
            # student.sex = cleaned_data['sex']
            # student.email = cleaned_data['email']
            # student.profession = cleaned_data['profession']
            # student.qq = cleaned_data['qq']
            # student.phone = cleaned_data['phone']
            # student.save()
            return HttpResponseRedirect(reverse('student_index'))
    else:
        form = StudentForm()
    
    context = {
        'students': students,
        'form': form
    }
    return render(request, 'student/index.html', context=context)

# 基于類的模闆
class Index(View):
    template_name = 'student/index.html'

    def get_context(self):
        context = {
            'students': Student.objects.all(),
        }
        return context

    def get(self, request):
        form = StudentForm()
        context = self.get_context()
        context.update({'form': form})
        return render(request, self.template_name, context=context)

    def post(self, request):
        form = StudentForm(request.POST)
        if form.is_valid():
            form.save()
            return HttpResponseRedirect(reverse('student_index'))
        context = self.get_context()
        context.update({'form': form})
        return render(request, self.template_name, context=context)
           

頁面顯示層(templates/*.html)

這裡會用到django自創的模闆指令,此外,django的模闆引擎還可以換成Jinja2,模型字段的引用可以參考官方文檔。視圖通過調用

render(request, template_name, context)

函數,來渲染模闆,進而得到response。

表單輸入(forms.py)

Django可以事先定義好表單,然後在模闆中使用。表單既可以繼承自forms.Form類,字段設定參考這裡,也可以繼承自forms.ModelForm,直接複用Model的代碼,詳情可參考官方文檔。此外,django的表單還可以對輸入的資料進行校驗。

中間件

Django的中間件在前端/後端之前被調用,而且會用于所有的請求/響應,Django架構提供了

django.utils.deprecaion.MiddlewareMixin

類以便于使用者自定義站點的中間件。

MiddlewareMixin

的接口按順序包括:

  1. process_request(self, request)

    :傳回None才會執行後續的方法和中間件,傳回HttpResonse則不會。
  2. process_view(self, request, func, *args, **kwargs)

  3. process_exception(self, request, exception)

  4. process_template_response(self, request, response)

    :使用了模闆才會調用。
  5. process_response(self, request, response)

    :視圖層業務處理或者模闆層渲染發生異常時才會調用。

process_response

似乎一定會被調用,但

process_view

等則不一定,例如不涉及業務處理,僅僅重新整理頁面的時候。

import time
from django.http import response
from django.urls import reverse
from django.utils.deprecation import MiddlewareMixin

class TimeItMiddleware(MiddlewareMixin):
    def process_request(self, request):
        self.start_time = time.time()
        return None

    def process_view(self, request, func, *args, **kwargs):
        if request.path != reverse('index'):
            return None

        start = time.time()
        response = func(request)
        costed = time.time() - start
        print('process view: {:.2f}'.format(costed))
        return response

    def process_exception(self, request, exception):
        pass

    def process_template_response(self, request, response):
        costed = time.time() - self.start_time
        print('render cost: {:.2f}'.format(costed))
        return response

    def process_response(self, request, response):
        costed = time.time() - self.start_time
        print('request to response cost: {:.2f}'.format(costed))
        return response
           

測試

Django提供了一個

django.test.TestCase

的基類,開發者可以繼承此類來實作自己的單元測試,如果不涉及資料庫(測試資料與生産資料是隔離的),則可以使用

SimpleTestCase

作為基類,測試類提供的接口包括:

  1. setUp(self)

    :初始化環境。
  2. test_xxxx(self)

    :待測試的方法,均會被執行。
  3. tearDown(self)

    :清理測試環境。
from django.forms.fields import EmailField
from django.test import TestCase
from .models import Student

class StudentTestCase(TestCase):
    def setUp(self):
        Student.objects.create(
            name='1号玩家',
            sex = 1,
            email = '[email protected]',
            profession = '法師',
            qq = '11',
            phone = '111'
        )

    def test_create_and_sex_status(self):
        student = Student.objects.create(
            name='2号玩家',
            sex = 2,
            email = '[email protected]',
            profession = '戰士',
            qq  = '22',
            phone = '222'
        )
        self.assertEqual(student.get_sex_display(), '女', '碼表有誤')

    def test_filter(self):
        Student.objects.create(
            name='3号玩家',
            sex = 1,
            email = '[email protected]',
            profession = '律師',
            qq = '33',
            phone = '333'
        )
        name = '3号玩家'
        students = Student.objects.filter(name=name)
        self.assertEqual(students.count(), 1, '過濾器有誤')

    def test_get_index(self):
        client = self.client
        response = client.get('/student/')
        self.assertEqual(response.status_code, 200, 'homepage not availabel')

    def test_post_index(self):
        client = self.client
        data = dict(
            name='4号玩家',
            sex = 4,
            email = '[email protected]',
            profession = '警察',
            qq = '44',
            phone = '444'
        )
        response = client.post('/student/', data)
        self.assertTrue(b'[email protected]' in response.content, 'post失敗')