Django CRM 從零開始:為初學者打造的逐步教學 (重新排版)
嗨,Django 新手們!準備好踏上開發一個實用且功能強大的 CRM (客戶關係管理) 系統的旅程了嗎?Django 是一個高效的 Web 框架,它能讓您快速地建立複雜的資料庫驅動型應用程式。本教學將帶領您從零開始,逐步搭建一個基礎的 CRM 系統,涵蓋專案的核心組件,並引入現代開發實踐。
注意: 本教學文章基於https://github.com/BpsEason/FullStackDjangoCRM.git的概念和結構,但為了提供更簡潔、易於理解的初學者體驗,某些檔案的內容(特別是 HTML 模板中的 JavaScript)已被簡化和優化,以專注於 Django 核心概念的教學。當您完成本教學後,您可以回到我的 GitHub 專案,探索更多進階功能和實戰範例,將其作為您進一步學習和提升的資源。
目標讀者
本教學適合對 Python 有基本了解,並對 Web 開發有興趣的 Django 初學者。
我們將構建什麼?
一個基礎的 CRM 系統,具備以下功能:
- 客戶管理: 追蹤客戶的基本資訊。
- 活動追蹤: 記錄與客戶相關的通話、會議等活動。
- 用戶認證: 確保只有登錄用戶才能訪問系統。
- 管理介面: 利用 Django Admin 快速管理資料。
- Docker 化開發環境: 使用 Docker Compose 輕鬆搭建開發環境。
為什麼選擇 Django?
- 快速開發: Django 提供了許多開箱即用的組件和約定,加速開發流程。
- ORM: 強大的物件關係映射 (ORM) 讓您無需撰寫原始 SQL 即可操作資料庫。
- Admin 介面: 自動生成的功能齊全的管理後台,極大地提高了開發效率。
- 安全性: 內建了多種安全保護機制,如 CSRF、XSS 防禦、密碼哈希等。
- 可擴展性: 從小型專案到大型複雜應用,Django 都能良好支持。
一、環境準備
在開始之前,請確保您的系統已安裝以下軟體:
- Python 3.8+
- pip (Python 套件管理器)
- Docker Desktop (包含 Docker Engine 和 Docker Compose)
- Git
二、專案初始化與結構概覽
我們將從一個空的專案開始。
-
創建專案目錄:
Bashmkdir FullStackDjangoCRM cd FullStackDjangoCRM
-
創建 .gitignore 檔案:
為了避免將不必要的檔案提交到版本控制,創建 .gitignore 檔案。
檔案內容: FullStackDjangoCRM/.gitignore
*.pyc __pycache__/ .env db.sqlite3 /staticfiles/ .DS_Store .idea/ .vscode/
-
創建 requirements.txt:
列出所有專案的 Python 依賴。
檔案內容: FullStackDjangoCRM/requirements.txt
django==5.1.1 djangorestframework==3.15.2 psycopg2-binary==2.9.9 redis==5.0.8 celery==5.4.0 argon2-cffi==23.1.0 django-environ==0.11.2 gunicorn==23.0.0 django-bootstrap5==24.2
-
創建 Dockerfile:
用於構建我們的 Django 應用程式 Docker 映像。
檔案內容: FullStackDjangoCRM/Dockerfile
DockerfileFROM python:3.11-slim-buster ENV PYTHONUNBUFFERED 1 ENV PYTHONDONTWRITEBYTECODE 1 WORKDIR /app COPY requirements.txt /app/ RUN pip install --no-cache-dir -r requirements.txt COPY . /app/ CMD ["gunicorn", "--bind", "0.0.0.0:8000", "crm.wsgi:application"]
-
創建 docker-compose.yaml:
定義我們的多容器開發環境,包括 Django 應用、PostgreSQL 資料庫、Redis 和 Nginx。
檔案內容: FullStackDjangoCRM/docker-compose.yaml
YAMLversion: '3.9' services: web: build: . command: gunicorn --bind 0.0.0.0:8000 crm.wsgi:application volumes: - .:/app - static_volume:/app/staticfiles # 持久化靜態檔案 ports: - "8000:8000" environment: # 從 .env 文件載入環境變數 - DEBUG=${DEBUG} - SECRET_KEY=${SECRET_KEY} - DB_NAME=${DB_NAME} - DB_USER=${DB_USER} - DB_PASSWORD=${DB_PASSWORD} - DB_HOST=${DB_HOST} - DB_PORT=${DB_PORT} - REDIS_URL=${REDIS_URL} - CELERY_BROKER_URL=${CELERY_BROKER_URL} - CELERY_RESULT_BACKEND=${CELERY_RESULT_BACKEND} depends_on: - db - redis networks: - crm_network db: image: postgres:16 environment: - POSTGRES_DB=${DB_NAME} - POSTGRES_USER=${DB_USER} - POSTGRES_PASSWORD=${DB_PASSWORD} volumes: - postgres_data:/var/lib/postgresql/data # 持久化資料庫資料 networks: - crm_network redis: image: redis:7 networks: - crm_network celery: build: . command: celery -A crm worker --loglevel=info # 運行 Celery worker volumes: - .:/app environment: - DEBUG=${DEBUG} - SECRET_KEY=${SECRET_KEY} - DB_NAME=${DB_NAME} - DB_USER=${DB_USER} - DB_PASSWORD=${DB_PASSWORD} - DB_HOST=${DB_HOST} - DB_PORT=${DB_PORT} - REDIS_URL=${REDIS_URL} - CELERY_BROKER_URL=${CELERY_BROKER_URL} - CELERY_RESULT_BACKEND=${CELERY_RESULT_BACKEND} depends_on: - db - redis networks: - crm_network nginx: image: nginx:1.25 volumes: - ./docker/nginx.conf:/etc/nginx/conf.d/default.conf:ro # 掛載 Nginx 配置 - static_volume:/static # Nginx 服務靜態檔案 ports: - "80:80" # 將主機的 80 端口映射到 Nginx 容器的 80 端口 depends_on: - web # 等待 web 服務啟動 networks: - crm_network volumes: postgres_data: static_volume: networks: crm_network: driver: bridge
-
創建 Nginx 配置目錄和檔案:
Bashmkdir docker
檔案內容:
FullStackDjangoCRM/docker/nginx.conf
Nginxserver { listen 80; server_name localhost; # 在生產環境請替換為你的域名 location /static/ { alias /static/; # 確保這個路徑與 docker-compose.yaml 中的靜態卷軸掛載路徑一致 expires 30d; # 靜態檔案緩存 30 天 add_header Cache-Control "public, no-transform"; } location / { proxy_pass http://web:8000; # 轉發請求到 Django 應用 (web 服務) proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }
-
創建 .env 檔案:
保存敏感信息和環境配置。
檔案內容: FullStackDjangoCRM/.env
SECRET_KEY=your_super_secret_key_here # 請替換為一個真正隨機且複雜的密鑰 DEBUG=True ALLOWED_HOSTS=localhost,127.0.0.1 DB_NAME=crm_db DB_USER=crm_user DB_PASSWORD=crm_password DB_HOST=db DB_PORT=5432 REDIS_URL=redis://redis:6379/0 CELERY_BROKER_URL=redis://redis:6379/0 CELERY_RESULT_BACKEND=redis://redis:6379/0 # 生產環境下應設置為 True 並啟用 HTTPS SECURE_SSL_REDIRECT=False SESSION_COOKIE_SECURE=False CSRF_COOKIE_SECURE=False SECURE_HSTS_SECONDS=0 SECURE_HSTS_INCLUDE_SUBDOMAINS=False SECURE_HSTS_PRELOAD=False
安全提示:
SECRET_KEY
必須是唯一且複雜的。在生產環境中,DEBUG
必須設置為False
,並且應啟用所有SECURE_
相關的設定(將False
改為True
,並為SECURE_HSTS_SECONDS
設置一個非零值)。 -
構建並啟動 Docker 服務:
Bashdocker compose build docker compose up -d
這將構建 Docker 映像並在後台啟動所有服務。
三、Django 專案核心設定
現在我們將進入 Django 專案內部。
-
創建 Django 專案:
Bashdocker compose exec web django-admin startproject crm .
這會在當前目錄 (
FullStackDjangoCRM
) 下創建一個名為crm
的 Django 專案,其中包含settings.py
,urls.py
等。 -
修改 crm/settings.py:
打開 crm/settings.py 檔案,進行以下關鍵修改。
-
導入
environ
和基礎路徑:Python# crm/settings.py import environ import os # 初始化 django-environ env = environ.Env() # 讀取 .env 文件,如果存在的話 environ.Env.read_env() # 定義專案的根目錄 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # 從環境變數獲取配置 SECRET_KEY = env('SECRET_KEY') DEBUG = env.bool('DEBUG', default=True) # 開發環境下預設為 True ALLOWED_HOSTS = env.list('ALLOWED_HOSTS', default=['localhost', '127.0.0.1'])
-
INSTALLED_APPS:
將您會用到的應用程式添加到這裡。
Python# crm/settings.py INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'rest_framework', # 如果未來要開發 API 'django_bootstrap5', # 整合 Bootstrap 'customers', # 我們自己的應用 'activities', # 我們自己的應用 # ... 其他您可能添加的應用 ]
-
MIDDLEWARE:
這是 Django 請求處理流程中的中間件,保持默認並確保安全相關的在最前面。
Python# crm/settings.py MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', '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', ]
-
ROOT_URLCONF:
指定專案的主 URL 配置文件。
Python# crm/settings.py ROOT_URLCONF = 'crm.urls'
-
TEMPLATES:
配置 Django 模板引擎。確保 DIRS 包含 'templates' 目錄,這樣可以存放共用模板。
Python# crm/settings.py TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates')], # 添加這個路徑來存放共用模板 'APP_DIRS': True, # 讓 Django 搜尋每個 app 的 templates 目錄 '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', ], }, }, ]
-
WSGI_APPLICATION 和 ASGI_APPLICATION:
指定應用程式的 WSGI 和 ASGI 入口點。
Python# crm/settings.py WSGI_APPLICATION = 'crm.wsgi.application' ASGI_APPLICATION = 'crm.asgi.application' # 如果未來需要 ASGI (例如用於 WebSocket)
-
DATABASES:
配置 PostgreSQL 資料庫,所有連接參數都從 .env 環境變數中獲取。
Python# crm/settings.py DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': env('DB_NAME'), 'USER': env('DB_USER'), 'PASSWORD': env('DB_PASSWORD'), 'HOST': env('DB_HOST'), # Docker 服務名稱 (在 docker-compose.yaml 中定義) 'PORT': env('DB_PORT'), } }
-
AUTH_PASSWORD_VALIDATORS:
用於強化密碼安全,這是一組 Django 內建的密碼驗證器。
Python# crm/settings.py AUTH_PASSWORD_VALIDATORS = [ {'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator'}, {'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator'}, {'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator'}, {'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator'}, ]
-
國1際化和時區:
設置語言和時區,方便未來應用程式的本地化。
Python# crm/settings.py LANGUAGE_CODE = 'zh-hant' # 設置為繁體中文 TIME_ZONE = 'Asia/Taipei' # 設置為台北時區 USE_I18N = True # 啟用國際化 USE_TZ = True # 啟用時區感知
-
靜態檔案配置:
配置 Django 如何處理 CSS、JavaScript 和圖片等靜態檔案。
Python# crm/settings.py STATIC_URL = '/static/' STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles') # 部署時,所有靜態檔案會被收集到此目錄 STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')] # 開發時,您可以將應用程式共用的靜態檔案放在這裡
-
緩存 (Redis):
配置 Redis 作為 Django 的預設緩存後端,提高應用程式效能。
Python# crm/settings.py CACHES = { 'default': { 'BACKEND': 'django_redis.cache.RedisCache', 'LOCATION': env('REDIS_URL'), 'OPTIONS': { 'CLIENT_CLASS': 'django_redis.client.DefaultClient', } } }
-
Celery 配置:
設置 Celery,用於處理異步任務(例如發送電子郵件或複雜的資料處理)。
Python# crm/settings.py CELERY_BROKER_URL = env('CELERY_BROKER_URL') CELERY_RESULT_BACKEND = env('CELERY_RESULT_BACKEND') CELERY_ACCEPT_CONTENT = ['json'] CELERY_TASK_SERIALIZER = 'json'
-
安全相關設定 (生產環境務必啟用):
這些設定在開發環境通常設置為 False,但在生產環境中為了安全,務必將其設置為 True 並確保您的網站使用 HTTPS。
Python# crm/settings.py SECURE_SSL_REDIRECT = env.bool('SECURE_SSL_REDIRECT', default=False) SESSION_COOKIE_SECURE = env.bool('SESSION_COOKIE_SECURE', default=False) CSRF_COOKIE_SECURE = env.bool('CSRF_COOKIE_SECURE', default=False) # HSTS (HTTP Strict Transport Security) 配置,防止降級攻擊 SECURE_HSTS_SECONDS = env.int('SECURE_HSTS_SECONDS', default=0) # 在生產環境設置為非零值 (例如 31536000 1年) SECURE_HSTS_INCLUDE_SUBDOMAINS = env.bool('SECURE_HSTS_INCLUDE_SUBDOMAINS', default=False) SECURE_HSTS_PRELOAD = env.bool('SECURE_HSTS_PRELOAD', default=False)
-
-
修改 crm/wsgi.py 和 crm/asgi.py:
這兩個檔案通常由 Django 自動生成,並且是正確的,無需額外修改。它們是應用程式與 Web 服務器溝通的標準接口。
crm/wsgi.py
內容:Pythonimport os from django.core.wsgi import get_wsgi_application os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'crm.settings') application = get_wsgi_application()
crm/asgi.py
內容:Pythonimport os from django.core.asgi import get_asgi_application os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'crm.settings') application = get_asgi_application()
-
修改 crm/urls.py (專案層級):
這是專案的總路由配置。我們將在此處包含各個應用程式的 URL。
檔案內容:
crm/urls.py
Pythonfrom django.contrib import admin from django.urls import path, include from django.contrib.auth import views as auth_views # 導入 Django 內建的認證視圖 urlpatterns = [ path('admin/', admin.site.urls), path('customers/', include('customers.urls')), path('activities/', include('activities.urls')), # 登入/登出 URL:使用 Django 內建的認證視圖 path('login/', auth_views.LoginView.as_view(template_name='registration/login.html'), name='login'), path('logout/', auth_views.LogoutView.as_view(), name='logout'), # 你可能還需要一個首頁 URL,例如: # path('', views.home, name='home'), ]
四、創建 Django 應用程式
我們將為客戶管理和活動追蹤分別創建兩個 Django 應用程式:customers
和 activities
。
-
創建應用程式:
在您的終端機中,確保您在 FullStackDjangoCRM 目錄下,然後運行以下命令來創建應用程式。
Bashdocker compose exec web python manage.py startapp customers docker compose exec web python manage.py startapp activities
這將在專案根目錄下創建兩個新的子目錄:
customers
和activities
,每個目錄都包含了 Django 應用程式的標準骨架檔案(如models.py
,views.py
等)。 -
定義模型 (Models):
模型是 Django 應用程式的核心,它定義了資料庫的結構以及如何與資料庫互動。
-
customers/models.py (客戶模型):
這個檔案定義了 Customer 模型的結構,它將映射到資料庫中的一個表。
檔案內容: FullStackDjangoCRM/customers/models.py
Pythonfrom django.db import models from django.contrib.auth.models import User # 引入 Django 的內建 User 模型 class Customer(models.Model): name = models.CharField(max_length=100, verbose_name="客戶名稱") email = models.EmailField(unique=True, blank=True, null=True, verbose_name="電子郵件") phone = models.CharField(max_length=20, blank=True, verbose_name="電話") address = models.TextField(blank=True, verbose_name="地址") # 透過 ForeignKey 關聯到 Django 內建的 User 模型,表示哪個用戶創建了這個客戶 # on_delete=models.CASCADE 表示如果關聯的用戶被刪除,則其創建的所有客戶也會被刪除 created_by = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="創建者") created_at = models.DateTimeField(auto_now_add=True, verbose_name="創建時間") # 首次保存時自動設置時間 updated_at = models.DateTimeField(auto_now=True, verbose_name="更新時間") # 每次保存時自動更新時間 class Meta: verbose_name = "客戶" # 在 Django Admin 中顯示的單數名稱 verbose_name_plural = "客戶" # 在 Django Admin 中顯示的複數名稱 ordering = ['name'] # 默認按照客戶名稱升序排序 def __str__(self): """ 定義模型的字串表示,方便在 Django Admin 或調試時識別物件。 """ return self.name
重要提示:
created_by
字段的添加是為了確保每個客戶記錄都與一個用戶關聯,這對於實現基於用戶的數據隔離(例如每個用戶只能看到自己的客戶)非常重要。 -
activities/models.py (活動模型):
這個檔案定義了 Activity 模型的結構,它將追蹤與客戶相關的各類活動。
檔案內容: FullStackDjangoCRM/activities/models.py
Pythonfrom django.db import models from django.contrib.auth.models import User from customers.models import Customer # 從客戶應用程式引入 Customer 模型 class Activity(models.Model): # 定義活動類型的選擇項。每個元組的第一個元素是儲存到資料庫的值,第二個是人類可讀的名稱。 ACTIVITY_TYPES = ( ('CALL', '電話'), ('MEETING', '會議'), ('TASK', '任務'), ('EMAIL', '電子郵件'), # 增加一個常見的活動類型 ) activity_type = models.CharField(max_length=20, choices=ACTIVITY_TYPES, verbose_name="活動類型") # 透過 ForeignKey 關聯到 Customer 模型,表示這個活動是關於哪個客戶的 customer = models.ForeignKey(Customer, on_delete=models.CASCADE, verbose_name="相關客戶") description = models.TextField(blank=True, verbose_name="描述") # 活動的詳細描述,允許為空 created_by = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="創建者") # 關聯創建該活動的用戶 created_at = models.DateTimeField(auto_now_add=True, verbose_name="創建時間") updated_at = models.DateTimeField(auto_now=True, verbose_name="更新時間") class Meta: verbose_name = "活動" verbose_name_plural = "活動" ordering = ['-created_at'] # 默認按照創建時間倒序排序(最新活動在前) def __str__(self): """ 定義模型的字串表示。 get_activity_type_display() 方法會返回 choices 中定義的人類可讀名稱。 """ return f"{self.get_activity_type_display()} - {self.customer.name}"
-
-
註冊應用程式:
您需要在 crm/settings.py 檔案中,將新創建的 customers 和 activities 應用程式添加到 INSTALLED_APPS 列表中,這樣 Django 才能識別它們。
這部分我們已經在之前的「修改 crm/settings.py」步驟中完成,請確保您的 settings.py 包含了它們:
Python# crm/settings.py (部分內容) INSTALLED_APPS = [ # ... 'customers', 'activities', # ... ]
-
執行資料庫遷移:
定義了模型之後,您需要告訴 Django 根據這些模型創建或修改資料庫表。
首先,創建遷移文件:
Bashdocker compose exec web python manage.py makemigrations
這會檢查您的模型定義,並生成相應的遷移文件(位於每個應用程式的 migrations 目錄下)。
然後,應用這些遷移到資料庫:
Bashdocker compose exec web python manage.py migrate
這會執行所有未應用的遷移,包括 Django 內建應用程式的遷移(如用戶、權限)以及您自己定義的
Customer
和Activity
模型的遷移。 -
創建超級用戶:
為了能夠登錄 Django Admin 介面和測試應用程式,您需要創建一個超級用戶。
Bashdocker compose exec web python manage.py createsuperuser
按照提示輸入您想要的用戶名、電子郵件(可選)和密碼。請記住這個用戶名和密碼,稍後會用到。
好的,這是教學文章的下一部分,關於 Django Admin 管理介面和視圖 (Views) 和 URL 配置。
五、Django Admin 管理介面
Django Admin 是一個非常強大且開箱即用的管理後台,它能讓您快速地查看、創建、編輯和刪除應用程式的資料,無需編寫額外的視圖或模板。
-
為模型註冊 Admin:
您需要告訴 Django Admin 哪些模型應該顯示在管理介面中。這通常在每個應用程式的 admin.py 檔案中完成。
-
customers/admin.py:
打開 FullStackDjangoCRM/customers/admin.py 並添加以下內容。
Python# customers/admin.py from django.contrib import admin from .models import Customer # 導入我們定義的 Customer 模型 # 使用 @admin.register 裝飾器來註冊模型到 Admin 介面 @admin.register(Customer) class CustomerAdmin(admin.ModelAdmin): # list_display 定義了在列表頁面顯示的字段 list_display = ('name', 'email', 'phone', 'created_by', 'created_at') # search_fields 允許您在 Admin 列表頁面中按這些字段進行搜索 search_fields = ('name', 'email', 'phone') # list_filter 允許您在 Admin 列表頁面中按這些字段進行過濾 list_filter = ('created_by',)
-
activities/admin.py:
打開 FullStackDjangoCRM/activities/admin.py 並添加以下內容。
Python# activities/admin.py from django.contrib import admin from .models import Activity # 導入我們定義的 Activity 模型 @admin.register(Activity) class ActivityAdmin(admin.ModelAdmin): list_display = ('activity_type', 'customer', 'created_by', 'created_at') search_fields = ('description',) # 允許按描述搜索 list_filter = ('activity_type', 'created_by') # 允許按活動類型和創建者過濾
-
-
訪問 Admin 介面:
現在,您的 Django 應用程式應該正在 Docker 容器中運行。您可以在瀏覽器中訪問以下 URL:
http://localhost/admin/
使用您之前創建的超級用戶的用戶名和密碼登錄。您應該能看到 "客戶" 和 "活動" 兩個選項,點擊它們即可開始管理資料。
六、視圖 (Views) 和 URL 配置
視圖是 Django 應用程式中處理業務邏輯的地方。它們接收來自 URL 的請求,與模型互動獲取資料,然後將資料傳遞給模板進行渲染。URL 配置則負責將特定的 URL 路徑映射到這些視圖。
-
customers/views.py (客戶視圖):
這個檔案將包含用於顯示客戶列表和客戶詳情的視圖函數。
檔案內容: FullStackDjangoCRM/customers/views.py
Pythonfrom django.shortcuts import render, get_object_or_404, redirect from django.contrib.auth.decorators import login_required # 確保只有登錄用戶才能訪問 from .models import Customer # from .forms import CustomerForm # 稍後我們可能會添加表單來處理數據提交 @login_required # 這個裝飾器要求用戶必須登錄才能訪問此視圖 def customer_list(request): """ 顯示當前登錄用戶創建的所有客戶列表。 """ # 過濾只顯示由當前請求用戶 (request.user) 創建的客戶 # order_by('-created_at') 表示按創建時間倒序排列(最新創建的客戶在前) customers = Customer.objects.filter(created_by=request.user).order_by('-created_at') return render(request, 'customers/customer_list.html', {'customers': customers}) @login_required def customer_detail(request, pk): """ 顯示特定客戶的詳細信息。 pk 是從 URL 中捕獲的客戶主鍵 (primary key)。 """ # 嘗試獲取指定 pk 的客戶,並且該客戶必須是由當前用戶創建的。 # 如果找不到,則返回 404 錯誤。 customer = get_object_or_404(Customer, pk=pk, created_by=request.user) return render(request, 'customers/customer_detail.html', {'customer': customer}) # 後續您可以根據需求添加更多視圖,例如用於創建、更新、刪除客戶的功能。
-
customers/urls.py (客戶 URL 配置):
這個檔案定義了 customers 應用程式內部的 URL 模式。
檔案內容: FullStackDjangoCRM/customers/urls.py
Pythonfrom django.urls import path from . import views # 從當前目錄導入 views.py # 定義應用程式命名空間,避免與其他應用程式的 URL 名稱衝突 app_name = 'customers' urlpatterns = [ # 當路徑為空字串 '' 時(例如 /customers/),調用 views.customer_list 視圖 # name='customer_list' 為這個 URL 模式指定一個名稱,方便在模板中引用 path('', views.customer_list, name='customer_list'), # 當路徑包含一個整數參數時(例如 /customers/1/),調用 views.customer_detail 視圖 # <int:pk> 會將匹配到的整數作為 pk 參數傳遞給視圖函數 path('<int:pk>/', views.customer_detail, name='customer_detail'), ]
-
activities/views.py (活動視圖):
這個檔案將包含用於顯示活動列表的視圖函數。
檔案內容: FullStackDjangoCRM/activities/views.py
Pythonfrom django.shortcuts import render from django.contrib.auth.decorators import login_required # 確保只有登錄用戶才能訪問 from .models import Activity @login_required # 要求用戶登錄 def activity_list(request): """ 顯示當前登錄用戶創建的所有活動列表。 """ # 過濾只顯示由當前請求用戶 (request.user) 創建的活動 # order_by('-created_at') 表示按創建時間倒序排列 activities = Activity.objects.filter(created_by=request.user).order_by('-created_at') return render(request, 'activities/activity_list.html', {'activities': activities}) # 後續您可以添加更多視圖,例如用於創建、更新、刪除活動的功能。
-
activities/urls.py (活動 URL 配置):
這個檔案定義了 activities 應用程式內部的 URL 模式。
檔案內容: FullStackDjangoCRM/activities/urls.py
Pythonfrom django.urls import path from . import views # 從當前目錄導入 views.py # 定義應用程式命名空間 app_name = 'activities' urlpatterns = [ # 當路徑為空字串 '' 時(例如 /activities/),調用 views.activity_list 視圖 path('', views.activity_list, name='activity_list'), ]
七、模板 (Templates)
模板是 Django 應用程式中呈現用戶界面的 HTML 檔案。它們使用 Django 模板語言來顯示來自視圖的資料,並繼承 base.html
來保持一致的網站佈局。
-
創建 templates 目錄:
在您的 Django 專案根目錄 (FullStackDjangoCRM) 下,創建一個名為 templates 的目錄。這個目錄將用於存放您專案範圍內的共用模板,例如 base.html。
Bashmkdir templates
-
創建應用程式專屬模板目錄:
在 templates 目錄下,為每個應用程式創建一個與應用程式同名的子目錄。
Bashmkdir templates/customers mkdir templates/activities mkdir templates/registration # 用於認證系統的模板
-
templates/base.html (基礎佈局模板):
這是您網站的基礎骨架,包含了導航欄、腳部、以及所有頁面共用的 CSS 和 JavaScript 連結。其他模板將會繼承這個基礎模板。
檔案內容: FullStackDjangoCRM/templates/base.html
HTML{% load static %} {# 加載靜態檔案相關的模板標籤 #} <!DOCTYPE html> <html lang="zh-Hant"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Django CRM - {% block title %}{% endblock %}</title> {# 頁面標題,由子模板填充 #} {# 引入 Bootstrap 5 CSS #} <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous"> {# 引入自定義 CSS (如果有的話) #} <link rel="stylesheet" href="{% static 'css/custom.css' %}"> </head> <body> <nav class="navbar navbar-expand-lg navbar-dark bg-dark"> <div class="container-fluid"> <a class="navbar-brand" href="#">Django CRM</a> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarNav"> <ul class="navbar-nav ms-auto"> {# 使用 ms-auto 將導航項目靠右對齊 #} {% if user.is_authenticated %} {# 判斷用戶是否已登錄 #} <li class="nav-item"> <a class="nav-link" href="{% url 'customers:customer_list' %}">客戶</a> </li> <li class="nav-item"> <a class="nav-link" href="{% url 'activities:activity_list' %}">活動</a> </li> <li class="nav-item"> <a class="nav-link" href="{% url 'admin:index' %}">管理</a> {# 指向 Django Admin 介面 #} </li> <li class="nav-item"> <a class="nav-link" href="{% url 'logout' %}">登出</a> </li> {% else %} <li class="nav-item"> <a class="nav-link" href="{% url 'login' %}">登入</a> </li> {% endif %} </ul> </div> </div> </nav> <div class="container mt-5"> {# 這是內容區塊,子模板會填充這裡 #} {% block content %} {% endblock %} </div> {# 引入 Bootstrap 5 JavaScript #} <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script> {# 引入自定義 JavaScript (如果有的話) #} <script src="{% static 'js/custom.js' %}"></script> </body> </html>
注意: 我已經將之前您提供的
base.html
中可能導致問題的 XLSX 相關的 JavaScript 程式碼移除,因為它們不屬於核心佈局,且通常應該放在單獨的 JavaScript 檔案中,或者僅在需要該功能的特定頁面引入。這樣base.html
更加簡潔和可讀。 -
templates/customers/customer_list.html (客戶列表模板):
這個模板用於顯示客戶列表。
檔案內容: FullStackDjangoCRM/templates/customers/customer_list.html
HTML{% extends 'base.html' %} {# 繼承 base.html 模板 #} {% block title %}客戶列表{% endblock %} {# 設定頁面標題 #} {% block content %} {# 填充 base.html 中定義的 content 區塊 #} <h1>客戶列表</h1> <table class="table table-striped"> {# 使用 Bootstrap 表格樣式 #} <thead> <tr> <th>姓名</th> <th>電子郵件</th> <th>電話</th> <th>操作</th> </tr> </thead> <tbody> {% for customer in customers %} {# 循環遍歷從視圖傳遞過來的 'customers' 列表 #} <tr> <td>{{ customer.name }}</td> <td>{{ customer.email }}</td> <td>{{ customer.phone }}</td> <td> {# 創建一個指向客戶詳情頁的連結,使用 Django 的 url 標籤和命名空間 #} <a href="{% url 'customers:customer_detail' customer.pk %}" class="btn btn-primary btn-sm">查看</a> </td> </tr> {% empty %} {# 如果 'customers' 列表為空,則顯示以下內容 #} <tr><td colspan="4">無客戶資料</td></tr> {% endfor %} </tbody> </table> {% endblock %}
注意: 同樣地,我也移除了您提供的
customer_list.html
中不屬於列表顯示核心的 JavaScript 程式碼。 -
templates/customers/customer_detail.html (客戶詳情模板):
這個模板用於顯示單個客戶的詳細資訊。
檔案內容: FullStackDjangoCRM/templates/customers/customer_detail.html
HTML{% extends 'base.html' %} {% block title %}客戶詳情 - {{ customer.name }}{% endblock %} {# 動態顯示客戶名稱作為標題 #} {% block content %} <h1>客戶詳情</h1> <div class="card"> {# 使用 Bootstrap 的卡片組件來美化顯示 #} <div class="card-body"> <h5 class="card-title">{{ customer.name }}</h5> <p class="card-text"><strong>電子郵件:</strong> {{ customer.email }}</p> <p class="card-text"><strong>電話:</strong> {{ customer.phone }}</p> <p class="card-text"><strong>地址:</strong> {{ customer.address }}</p> <p class="card-text"><strong>創建者:</strong> {{ customer.created_by }}</p> <p class="card-text"><strong>創建時間:</strong> {{ customer.created_at }}</p> {# 返回客戶列表頁的連結 #} <a href="{% url 'customers:customer_list' %}" class="btn btn-secondary mt-3">返回客戶列表</a> </div> </div> {% endblock %}
注意: 同樣地,我也移除了您提供的
customer_detail.html
中不屬於詳情顯示核心的 JavaScript 程式碼。 -
templates/activities/activity_list.html (活動列表模板):
這個模板用於顯示活動列表。
檔案內容: FullStackDjangoCRM/templates/activities/activity_list.html
HTML{% extends 'base.html' %} {% block title %}活動列表{% endblock %} {% block content %} <h1>活動列表</h1> <table class="table table-striped"> <thead> <tr> <th>類型</th> <th>客戶</th> <th>描述</th> <th>創建時間</th> </tr> </thead> <tbody> {% for activity in activities %} <tr> {# activity.get_activity_type_display 會返回 choices 中定義的人類可讀名稱 #} <td>{{ activity.get_activity_type_display }}</td> <td>{{ activity.customer.name }}</td> {# 透過 ForeignKey 訪問相關客戶的名稱 #} <td>{{ activity.description }}</td> <td>{{ activity.created_at }}</td> </tr> {% empty %} <tr><td colspan="4">無活動資料</td></tr> {% endfor %} </tbody> </table> {% endblock %}
注意: 同樣地,我也移除了您提供的
activity_list.html
中不屬於列表顯示核心的 JavaScript 程式碼。
好的,這是教學文章的下一部分,關於認證系統 (Authentication System) 和 測試 (Testing)。
八、認證系統 (Authentication System)
Django 內建了一個功能強大且易於使用的用戶認證系統,它處理了用戶註冊、登錄、登出、密碼重置等常見功能。我們只需要配置好 URL 和模板,就可以利用它。
-
添加登入/登出 URL 到專案層級 urls.py:
在 crm/urls.py 中,我們已經添加了指向 Django 內建認證視圖的 URL。這裡我們將更詳細地解釋它們。
檔案內容:
FullStackDjangoCRM/crm/urls.py
(此處僅顯示新增或修改部分)Python# ... (其他 import 語句) from django.contrib.auth import views as auth_views # 導入 Django 內建的認證視圖 urlpatterns = [ # ... (您之前定義的其他 URL) # 登入 URL: # 使用 Django 內建的 LoginView。 # template_name='registration/login.html' 指定了登入頁面使用的模板檔案。 # name='login' 為這個 URL 模式指定一個名稱,方便在模板中引用。 path('login/', auth_views.LoginView.as_view(template_name='registration/login.html'), name='login'), # 登出 URL: # 使用 Django 內建的 LogoutView。 # LogoutView 預設會將用戶導向到 settings.LOGOUT_REDIRECT_URL 或一個名為 '/' 的 URL。 path('logout/', auth_views.LogoutView.as_view(), name='logout'), # 你可能還需要一個登入後的首頁,例如: # path('', views.home, name='home'), ]
-
創建登入模板:
Django 內建的 LoginView 預設會尋找位於 registration/ 目錄下的 login.html 模板。我們需要在 templates/registration/ 目錄中創建這個檔案。
檔案內容:
FullStackDjangoCRM/templates/registration/login.html
HTML{% extends 'base.html' %} {# 繼承基礎佈局 #} {% block title %}登入{% endblock %} {# 設定頁面標題 #} {% block content %} <div class="row justify-content-center"> {# 使用 Bootstrap 居中佈局 #} <div class="col-md-6"> {# 設置列寬 #} <div class="card"> {# 使用卡片組件 #} <div class="card-header">登入</div> {# 卡片標題 #} <div class="card-body"> {# 卡片內容 #} <form method="post"> {# 登入表單,使用 POST 方法提交 #} {% csrf_token %} {# Django CSRF 保護,必不可少 #} {# form.as_p 會將表單字段渲染為 <p> 標籤。 您也可以手動渲染每個字段以獲得更精細的控制,例如: <div class="mb-3"> <label for="{{ form.username.id_for_label }}" class="form-label">用戶名:</label> {{ form.username }} </div> <div class="mb-3"> <label for="{{ form.password.id_for_label }}" class="form-label">密碼:</label> {{ form.password }} </div> #} {{ form.as_p }} <button type="submit" class="btn btn-primary">登入</button> </form> </div> </div> </div> </div> {% endblock %}
說明: 當
LoginView
被調用時,它會自動實例化一個AuthenticationForm
(用於處理用戶名和密碼輸入)並將其傳遞給模板,名稱為form
。
九、測試 (Testing)
測試是軟體開發中至關重要的一環,它能幫助您驗證程式碼的行為是否符合預期,及早發現錯誤,並確保在修改程式碼後不會引入新的問題(迴歸測試)。Django 提供了一個強大的測試框架。
-
activities/tests.py:
我們將為 Activity 模型編寫一個簡單的單元測試。
檔案內容:
FullStackDjangoCRM/activities/tests.py
Pythonfrom django.test import TestCase # 導入 Django 的測試基類 from django.contrib.auth.models import User # 導入 Django 內建的 User 模型 from customers.models import Customer # 導入 Customer 模型 from .models import Activity # 導入 Activity 模型 class ActivityModelTests(TestCase): """ 為 Activity 模型編寫測試。 """ def setUp(self): """ 在每個測試方法運行之前準備測試資料。 """ # 創建一個測試用戶,用於模擬創建客戶和活動 self.user = User.objects.create_user(username='testuser', password='password123') # 創建一個測試客戶,並關聯到上述測試用戶 self.customer = Customer.objects.create( name='Test Customer', email='test@example.com', created_by=self.user # 關聯到創建者 ) # 創建一個測試活動,並關聯到測試用戶和客戶 self.activity = Activity.objects.create( activity_type='CALL', # 使用模型中定義的 CHOICE 值 customer=self.customer, description='Test call for test customer.', created_by=self.user ) def test_activity_str(self): """ 測試 Activity 模型的 __str__ 方法是否返回正確的字串表示。 """ # 預期的字串應該是 "電話 - Test Customer" # 這取決於 Activity 模型中 ACTIVITY_TYPES 的定義,如果 'CALL' 對應 '電話'。 # 請檢查您 models.py 中的定義: # ('CALL', '電話') self.assertEqual(str(self.activity), '電話 - Test Customer') def test_activity_creation(self): """ 測試 Activity 對象是否成功創建並儲存到資料庫,以及其屬性是否正確。 """ # 驗證資料庫中 Activity 模型的實例數量為 1 self.assertEqual(Activity.objects.count(), 1) # 從資料庫中獲取剛創建的 Activity 對象 new_activity = Activity.objects.get(activity_type='CALL') # 驗證新活動的客戶名稱和創建者用戶名是否正確 self.assertEqual(new_activity.customer.name, 'Test Customer') self.assertEqual(new_activity.created_by.username, 'testuser') # 您可以根據需要添加更多測試方法,例如: # - test_activity_update:測試活動更新功能 # - test_activity_delete:測試活動刪除功能 # - test_activity_list_view:測試活動列表頁面是否正確顯示數據 # - test_login_required_for_views:測試未登錄用戶是否被重定向到登錄頁面
-
運行測試:
在您的終端機中,確保您在 FullStackDjangoCRM 目錄下,然後運行以下命令來執行測試:
Bashdocker compose exec web python manage.py test
Django 將會發現並運行所有應用程式中的
tests.py
文件中的測試。如果所有測試都通過,您將會看到類似 "OK" 的輸出。
十、下一步和進階概念
恭喜您!您已經成功建立了一個基礎的 Django CRM 系統,並學習了如何使用 Docker Compose 進行開發。這只是冰山一角,Django 世界還有許多值得探索的地方。
-
表單 (Forms):
目前我們還沒有提供創建或編輯客戶/活動的表單。您可以為 Customer 和 Activity 模型創建 ModelForm,以處理用戶輸入和驗證。
Python# customers/forms.py (範例,您需要自己創建這個檔案) from django import forms from .models import Customer, Activity class CustomerForm(forms.ModelForm): class Meta: model = Customer fields = ['name', 'email', 'phone', 'address'] # 不包含 created_by, created_at, updated_at,這些會自動處理 class ActivityForm(forms.ModelForm): class Meta: model = Activity fields = ['activity_type', 'customer', 'description'] # created_by, created_at, updated_at 也會自動處理
然後在您的視圖 (
views.py
) 中使用這些表單來處理 POST 請求。 -
CRUD 功能:
為客戶和活動實現完整的 CRUD (創建、讀取、更新、刪除) 功能。可以利用 Django 的 Class-Based Views (CBV),如 CreateView, UpdateView, DeleteView,它們能大大簡化程式碼並提供統一的結構。
-
分頁 (Pagination):
當資料量很大時,實現分頁來提高列表頁的載入速度和用戶體驗。Django 內建了分頁器。
-
Celery 異步任務:
利用 Celery 處理耗時任務,如發送郵件通知、生成大型報告、數據導入導出等,避免阻塞用戶界面。
要配置 Celery,您需要:
- 在
crm/
目錄下創建celery.py
檔案: 檔案內容:FullStackDjangoCRM/crm/celery.py
Pythonimport os from celery import Celery # 設置 Django settings 模組的路徑 os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'crm.settings') app = Celery('crm') # 使用 Django settings 中的配置來配置 Celery (例如 CELERY_BROKER_URL) app.config_from_object('django.conf:settings', namespace='CELERY') # 自動發現所有已註冊 Django 應用程式中的任務 app.autodiscover_tasks()
- 在
crm/__init__.py
中導入它,確保 Celery 應用程式在 Django 啟動時加載: 檔案內容:FullStackDjangoCRM/crm/__init__.py
Pythonfrom .celery import app as celery_app __all__ = ('celery_app',)
- 在您的應用程式(例如
activities
)中創建tasks.py
檔案來定義異步任務: 然後您可以在視圖中調用Python# activities/tasks.py (範例) from celery import shared_task import time @shared_task def send_email_notification(customer_id, message): """ 模擬發送電子郵件的異步任務。 """ # 這裡應該是發送郵件的實際邏輯 print(f"Sending email to customer {customer_id}: {message}") time.sleep(5) # 模擬耗時操作 print(f"Email sent to customer {customer_id}.") return f"Email sent successfully to customer {customer_id}"
send_email_notification.delay(customer.id, '您的活動已更新')
來非同步執行任務。
- 在
-
API 開發 (Django REST Framework):
如果未來需要與移動應用或前端框架 (如 React, Vue.js) 集成,可以學習 Django REST Framework (DRF) 來構建 RESTful API。DRF 可以讓您非常快速地創建功能強大的 API 端點。
-
更細緻的權限控制:
Django 內建的權限系統已經很強大,但對於更複雜的角色和權限需求,可以探索第三方庫如 django-rules 或 Django Guardian。
-
部署到生產環境:
這是一個完整的專案,包含了 Dockerfile, docker-compose.yaml, nginx.conf 等,為生產部署打下了基礎。在部署到生產環境時,請務必:
- 將
DEBUG
設置為False
。 - 更新
SECRET_KEY
為一個更安全的隨機字串。 - 將
ALLOWED_HOSTS
設置為您的實際域名。 - 啟用並配置
SECURE_SSL_REDIRECT
,SESSION_COOKIE_SECURE
,CSRF_COOKIE_SECURE
和SECURE_HSTS_SECONDS
等安全設定。 - 使用 SSL/TLS 證書 (例如 Let's Encrypt) 來啟用 HTTPS,並在 Nginx 配置中相應設置。
- 考慮使用雲服務供應商 (如 AWS ECS/EC2, Google Cloud Run/Compute Engine, Azure App Service) 來部署 Docker 容器,並使用更健壯的日誌和監控解決方案。
- 將
結語
本教學為您提供了一個 Django CRM 專案的基礎框架和逐步指南。通過實踐,您不僅會學習到 Django 的核心概念,還會接觸到現代 Web 開發中重要的工具和最佳實踐,如 Docker、環境變數管理、緩存和異步任務。
記住,軟體開發是一個持續學習和迭代的過程。多動手,多嘗試,祝您在 Django 的世界中玩得開心!
沒有留言:
張貼留言