1.樣版程式:suijing_village/templates/index.html
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 55 | <!DOCTYPE html>
<html lang="zh-Hant">
<head>
<meta charset="UTF-8">
<title>水井村導覽</title>
<style>
body { font-family: system-ui; max-width: 700px; margin: 40px auto; padding: 0 20px; }
h1 { color: #1e5955; text-align: center; }
.card { background: #f8f9fa; padding: 20px; border-radius: 12px; margin: 20px 0; box-shadow: 0 2px 8px rgba(0,0,0,0.1); }
.btn { padding: 10px 20px; background: #2c7a77; color: white; border: none; border-radius: 8px; cursor: pointer; }
.btn:hover { background: #1e5955; }
.taste-btn { margin: 8px; padding: 10px 18px; background: #e0f2f1; border: 1px solid #b2dfdb; border-radius: 999px; cursor: pointer; }
.taste-btn.active { background: #80cbc4; }
</style>
</head>
<body>
<h1>水井村導覽</h1>
<div class="card">
<h3>歡迎!{% if is_first_visit %}初次造訪的旅人{% else %}第 <strong>{{ visit_count }}</strong> 次回訪{% endif %}</h3>
<p>現在時間:{{ now }}</p>
</div>
<div class="card">
<h3>目前導覽進度</h3>
<p>正在參觀: <strong>{{ current_place }}</strong></p>
<p>進度:{{ current_step }} / {{ total_steps }}</p>
{% if current_step < total_steps %}
<a href="{% url 'village:next_step' %}"><button class="btn">前往下一站 →</button></a>
{% else %}
<p style="color:#2c7a77;">恭喜!你已完成水井村全路線!</p>
{% endif %}
</div>
<div class="card">
<h3>你最喜歡的水井產品</h3>
<p>目前選擇: <strong>{{ favorite_taste }}</strong></p>
<form method="post" action="{% url 'village:choose_taste' %}">
{% csrf_token %}
<button type="submit" name="taste" value="蕃茄" class="taste-btn {% if favorite_taste == '蕃茄' %}active{% endif %}">蕃茄</button>
<button type="submit" name="taste" value="工作蝦" class="taste-btn {% if favorite_taste == '工作蝦' %}active{% endif %}">工作蝦</button>
<button type="submit" name="taste" value="文蛤" class="taste-btn {% if favorite_taste == '文蛤' %}active{% endif %}">文蛤</button>
<button type="submit" name="taste" value="其他" class="taste-btn {% if favorite_taste == '其他' %}active{% endif %}">其他</button>
</form>
</div>
<div style="text-align:center; margin-top:40px;">
<a href="{% url 'village:reset' %}"><button class="btn" style="background:#c62828;">重置我的水井村之旅</button></a>
</div>
</body>
</html>
|
2.視域程式:suijing_village/village/views.py
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 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | # village/views.py
from django.shortcuts import render, redirect
from django.http import HttpResponse
from datetime import datetime
def home(request):
# 確保 session 已初始化(Django 會自動處理,但有時需要強制觸發)
if not request.session.session_key:
request.session.create() # 手動建立 session key(進階用法)
# === 來訪次數 ===
visit_count = request.session.get('suijing_visit_count', 0) + 1
request.session['suijing_visit_count'] = visit_count
# === 使用者目前路線進度(多步驟導覽) ===
current_route_step = request.session.get('current_route_step', 1)
route_steps = [
"起點:水井奉天宮",
"步驟2:水井姻緣花",
"步驟3:水井柴井車",
"終點:風雲客棧"
]
# === 使用者偏好:喜歡的產品 ===
favorite_taste = request.session.get('favorite_well_taste', '尚未選擇')
context = {
'visit_count': visit_count,
'is_first_visit': visit_count == 1,
'current_step': current_route_step,
'current_place': route_steps[current_route_step - 1] if current_route_step <= len(route_steps) else "已完成全部路線",
'total_steps': len(route_steps),
'favorite_taste': favorite_taste,
'now': datetime.now().strftime("%Y-%m-%d %H:%M"),
}
return render(request, 'index.html', context)
def next_step(request):
"""下一站導覽"""
current = request.session.get('current_route_step', 1)
request.session['current_route_step'] = current + 1
# 可選:設定更短的 session 過期時間(例如導覽 2 小時內有效)
# request.session.set_expiry(60 * 60 * 2)
return redirect('village:home')
def choose_taste(request):
"""選擇喜歡的井水口味"""
if request.method == 'POST':
taste = request.POST.get('taste')
if taste in ['蕃茄', '工作蝦', '文蛤', '其他']:
request.session['favorite_well_taste'] = taste
# 進階:可以把偏好設成較長存活時間
request.session.set_expiry(60 * 60 * 24 * 30) # 30 天
return redirect('village:home')
def reset_journey(request):
"""重置我的水井村之旅"""
# 方法一:刪除特定 key
# request.session.pop('suijing_visit_count', None)
# request.session.pop('current_route_step', None)
# request.session.pop('favorite_well_taste', None)
# 方法二:全部清空(最乾脆)
request.session.flush() # 清空所有 session 資料,但保留 session key
# request.session.clear() # 另一種寫法(Django 4.0+ 推薦)
return redirect('village:home')
def debug_session(request):
"""開發時用的除錯頁面"""
return HttpResponse(
f"<pre>{dict(request.session)}</pre>"
"<br><a href='/'>回首頁</a>"
)
|
3.app路由器:suijing_village/village/urls.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | # village/urls.py
from django.urls import path
from . import views
app_name = 'village'
urlpatterns = [
path('', views.home, name='home'),
path('next/', views.next_step, name='next_step'),
path('choose-taste/', views.choose_taste, name='choose_taste'),
path('reset/', views.reset_journey, name='reset'),
path('debug/', views.debug_session, name='debug_session'), # 開發用
]
|
3.主路由器:suijing_village/suijing_village/urls.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | """suijing_village URL Configuration The `urlpatterns` list routes URLs to views. For more information please see: https://docs.djangoproject.com/en/4.0/topics/http/urls/ Examples: Function views 1. Add an import: from my_app import views 2. Add a URL to urlpatterns: path('', views.home, name='home') Class-based views 1. Add an import: from other_app.views import Home 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') Including another URLconf 1. Import the include() function: from django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ from django.contrib import admin from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), path('', include('village.urls')), ] |
4.設定檔案:suijing_village/suijing_village/settings.py
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 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | """ Django settings for suijing_village project. Generated by 'django-admin startproject' using Django 4.0.6. For more information on this file, see https://docs.djangoproject.com/en/4.0/topics/settings/ For the full list of settings and their values, see https://docs.djangoproject.com/en/4.0/ref/settings/ """ from pathlib import Path # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/4.0/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = 'django-insecure-5d5y6c=nx8d1x+tn!r2!tl7fnlj)6_+l2_o90czc#d907!q6df' # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True ALLOWED_HOSTS = ['cmlin.pythonanywhere.com'] # Application definition INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'village', ] 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 = 'suijing_village.urls' TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [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', ], }, }, ] WSGI_APPLICATION = 'suijing_village.wsgi.application' # Database # https://docs.djangoproject.com/en/4.0/ref/settings/#databases DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': BASE_DIR / 'db.sqlite3', } } # Password validation # https://docs.djangoproject.com/en/4.0/ref/settings/#auth-password-validators 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', }, ] # Internationalization # https://docs.djangoproject.com/en/4.0/topics/i18n/ LANGUAGE_CODE = 'en-us' TIME_ZONE = 'UTC' USE_I18N = True USE_TZ = True # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/4.0/howto/static-files/ STATIC_URL = 'static/' # Default primary key field type # https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' # default static files settings for PythonAnywhere. # see https://help.pythonanywhere.com/pages/DjangoStaticFiles for more info MEDIA_ROOT = '/home/cmlin/suijing_village/media' MEDIA_URL = '/media/' STATIC_ROOT = '/home/cmlin/suijing_village/static' STATIC_URL = '/static/' |
沒有留言:
張貼留言