此次專案是練習使用Claude 3.5 Sonnet建置設備管理系統,因此用這篇文章紀錄整個過程
- 用戶認證系統
- 設備列表和詳情頁面
- 預約功能
- 管理員界面
非常好,我很高興能夠帶領您開始開發這個實驗室設備管理系統。我們將按照軟件開發的標準流程來進行,從環境設置開始,然後逐步實現核心功能。讓我們開始吧!
步驟 1: 設置開發環境
首先,我們需要設置我們的開發環境。我們將使用 Python 和 Django 來開發這個系統。
- 安裝 Python(如果尚未安裝)
- 創建虛擬環境
- 安裝 Django 和其他必要的包
讓我們執行以下命令:
# 創建專案目錄
mkdir lab_management_system
cd lab_management_system
# 創建虛擬環境
python -m venv venv
# 激活虛擬環境
source venv/bin/activate # 在 Windows 上使用 venv\\\\Scripts\\\\activate
# 安裝 Django 和其他必要的包
pip install django
pip install mysqlclient # 用於 MySQL 數據庫連接
pip install django-crispy-forms # 用於美化表單
pip install crispy-bootstrap4 #網頁套件,後來追加安裝的
步驟 2: 創建 Django 項目和應用
現在我們來創建 Django 項目和應用:
# 創建 Django 項目
django-admin startproject lab_management .
# 創建 Django 應用
python manage.py startapp equipment
步驟 3: 配置設置
編輯 lab_management/settings.py
文件,添加我們的應用和設置數據庫:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'equipment', # 添加我們的應用
'crispy_forms',
]
# 數據庫設置(這裡使用 SQLite 作為示例,實際部署時可以換成 MySQL)
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
# 設置自定義用戶模型
AUTH_USER_MODEL = 'equipment.User'
# Crispy Forms 設置
CRISPY_TEMPLATE_PACK = 'bootstrap4'
步驟 4: 創建模型
在 equipment/models.py
文件中,我們將定義我們的數據模型:
from django.db import models
from django.contrib.auth.models import AbstractUser
from django.core.exceptions import ValidationError
from django.utils import timezone
class User(AbstractUser):
ROLE_CHOICES = (
('admin', '管理員'),
('user', '一般使用者'),
)
role = models.CharField(max_length=10, choices=ROLE_CHOICES, default='user')
def __str__(self):
return f"{self.username} ({self.get_role_display()})"
class Equipment(models.Model):
name = models.CharField(max_length=100, verbose_name="設備名稱")
description = models.TextField(blank=True, null=True, verbose_name="設備描述")
is_available = models.BooleanField(default=True, verbose_name="是否可用")
def __str__(self):
return self.name
class Meta:
verbose_name = "設備"
verbose_name_plural = "設備"
class AdditionalEquipment(models.Model):
name = models.CharField(max_length=50, verbose_name="附加設備名稱")
def __str__(self):
return self.name
class Meta:
verbose_name = "附加設備"
verbose_name_plural = "附加設備"
class Reservation(models.Model):
equipment = models.ForeignKey(Equipment, on_delete=models.CASCADE, verbose_name="主要設備")
user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="預約用戶")
test_name = models.CharField(max_length=100, verbose_name="測試名稱")
temperature = models.FloatField(verbose_name="溫度")
start_time = models.DateTimeField(verbose_name="開始時間")
end_time = models.DateTimeField(verbose_name="結束時間")
additional_equipment = models.ManyToManyField(AdditionalEquipment, blank=True, verbose_name="附加設備")
notified = models.BooleanField(default=False, verbose_name="是否已通知")
def __str__(self):
return f"{self.equipment.name} - {self.test_name} by {self.user.username}"
def clean(self):
if self.start_time >= self.end_time:
raise ValidationError("結束時間必須晚於開始時間")
# 檢查時間衝突
overlapping_reservations = Reservation.objects.filter(
equipment=self.equipment,
start_time__lt=self.end_time,
end_time__gt=self.start_time
).exclude(pk=self.pk)
if overlapping_reservations.exists():
raise ValidationError("此時間段已被預約")
class Meta:
verbose_name = "預約"
verbose_name_plural = "預約"
步驟 5: 創建和應用數據庫遷移
運行以下命令來創建和應用數據庫遷移:
python manage.py makemigrations
python manage.py migrate
這些步驟將為我們的項目建立基本的結構和數據模型。接下來,我們將開始實現視圖和模板,以及用戶認證系統。
您希望我們接下來關注哪個具體功能的實現?例如:
- 用戶認證系統
- 設備列表和詳情頁面
- 預約功能
- 管理員界面
請告訴我您想優先實現哪個功能,我們將繼續開發過程。
好的,讓我們來實現用戶認證系統。我們將使用 Django 的內建認證系統,並進行一些自定義以滿足我們的需求。我們會創建登錄、註冊和登出功能,以及一個簡單的用戶個人資料頁面。
步驟 1: 創建表單
首先,在 equipment
目錄下創建一個新文件 forms.py
,並添加以下代碼:
from django import forms
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
from .models import User
class CustomUserCreationForm(UserCreationForm):
email = forms.EmailField(required=True)
class Meta:
model = User
fields = ("username", "email", "password1", "password2")
def save(self, commit=True):
user = super().save(commit=False)
user.email = self.cleaned_data["email"]
if commit:
user.save()
return user
class CustomAuthenticationForm(AuthenticationForm):
username = forms.CharField(label="用戶名或電子郵件")
def clean_username(self):
username = self.cleaned_data['username']
if '@' in username:
try:
user = User.objects.get(email=username)
return user.username
except User.DoesNotExist:
raise forms.ValidationError("沒有與此電子郵件關聯的用戶。")
return username
步驟 2: 創建視圖
在 equipment/views.py
文件中添加以下代碼:
from django.shortcuts import render, redirect
from django.contrib.auth import login, authenticate, logout
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from .forms import CustomUserCreationForm, CustomAuthenticationForm
from .models import User
def register_view(request):
if request.method == "POST":
form = CustomUserCreationForm(request.POST)
if form.is_valid():
user = form.save()
login(request, user)
messages.success(request, "註冊成功!")
return redirect('profile')
else:
form = CustomUserCreationForm()
return render(request, 'equipment/register.html', {'form': form})
def login_view(request):
if request.method == "POST":
form = CustomAuthenticationForm(request, data=request.POST)
if form.is_valid():
username = form.cleaned_data.get('username')
password = form.cleaned_data.get('password')
user = authenticate(username=username, password=password)
if user is not None:
login(request, user)
messages.info(request, f"您已登入為 {username}。")
return redirect('profile')
else:
messages.error(request, "無效的用戶名或密碼。")
else:
messages.error(request, "無效的用戶名或密碼。")
form = CustomAuthenticationForm()
return render(request, 'equipment/login.html', {'form': form})
@login_required
def profile_view(request):
return render(request, 'equipment/profile.html', {'user': request.user})
def logout_view(request):
logout(request)
messages.info(request, "您已成功登出。")
return redirect('login')
步驟 3: 創建 URL 配置
在 equipment
目錄下創建一個新文件 urls.py
,並添加以下代碼:
from django.urls import path
from . import views
urlpatterns = [
path('register/', views.register_view, name='register'),
path('login/', views.login_view, name='login'),
path('logout/', views.logout_view, name='logout'),
path('profile/', views.profile_view, name='profile'),
]
然後,在主項目的 lab_management/urls.py
文件中添加以下內容:
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('equipment.urls')),
]
步驟 4: 創建模板
在 equipment
目錄下創建一個新的 templates/equipment
目錄,然後在其中創建以下 HTML 文件:
base.html:
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}實驗室設備管理系統{% endblock %}</title>
<link rel="stylesheet" href="<https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css>">
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<a class="navbar-brand" href="{% url 'profile' %}">實驗室設備管理</a>
<div class="navbar-nav ml-auto">
{% if user.is_authenticated %}
<a class="nav-item nav-link" href="{% url 'profile' %}">個人資料</a>
<a class="nav-item nav-link" href="{% url 'logout' %}">登出</a>
{% else %}
<a class="nav-item nav-link" href="{% url 'login' %}">登入</a>
<a class="nav-item nav-link" href="{% url 'register' %}">註冊</a>
{% endif %}
</div>
</nav>
<div class="container mt-4">
{% if messages %}
{% for message in messages %}
<div class="alert alert-{{ message.tags }}">
{{ message }}
</div>
{% endfor %}
{% endif %}
{% block content %}
{% endblock %}
</div>
<script src="<https://code.jquery.com/jquery-3.5.1.slim.min.js>"></script>
<script src="<https://cdn.jsdelivr.net/npm/@popperjs/core@2.5.3/dist/umd/popper.min.js>"></script>
<script src="<https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js>"></script>
</body>
</html>
register.html:
{% extends 'equipment/base.html' %}
{% load crispy_forms_tags %}
{% block title %}註冊 - {{ block.super }}{% endblock %}
{% block content %}
<h2>註冊</h2>
<form method="post">
{% csrf_token %}
{{ form|crispy }}
<button type="submit" class="btn btn-primary">註冊</button>
</form>
{% endblock %}
login.html:
{% extends 'equipment/base.html' %}
{% load crispy_forms_tags %}
{% block title %}登入 - {{ block.super }}{% endblock %}
{% block content %}
<h2>登入</h2>
<form method="post">
{% csrf_token %}
{{ form|crispy }}
<button type="submit" class="btn btn-primary">登入</button>
</form>
{% endblock %}
profile.html:
{% extends 'equipment/base.html' %}
{% block title %}個人資料 - {{ block.super }}{% endblock %}
{% block content %}
<h2>個人資料</h2>
<p>用戶名: {{ user.username }}</p>
<p>電子郵件: {{ user.email }}</p>
<p>角色: {{ user.get_role_display }}</p>
{% endblock %}
步驟 5: 更新設置
在 lab_management/settings.py
文件中添加以下內容:
LOGIN_URL = 'login'
LOGIN_REDIRECT_URL = 'profile'
LOGOUT_REDIRECT_URL = 'login'
這些設置定義了登入、登出後的重定向 URL。
步驟 6: 創建超級用戶(如果還沒有的話)
python manage.py createsuperuser
步驟 7: 運行服務器並測試
python manage.py runserver
現在,您可以訪問以下 URL 來測試用戶認證系統:
- http://127.0.0.1:8000/register/ (註冊)
- http://127.0.0.1:8000/login/ (登入)
- http://127.0.0.1:8000/profile/ (個人資料,需要登入)
- http://127.0.0.1:8000/logout/ (登出)
這樣,我們就完成了基本的用戶認證系統。您可以註冊新用戶,登入,查看個人資料,以及登出。系統還包括了基本的錯誤處理和消息顯示。
接下來,我們可以繼續完善系統,例如添加密碼重置功能,或者開始實現設備管理和預約系統的功能。您希望下一步做什麼?
好的,讓我們來開發管理員界面。Django 提供了一個強大的內建管理界面,我們可以對其進行自定義以滿足我們的需求。我們將為我們的模型創建管理界面,並添加一些自定義功能。
步驟 1: 註冊模型到管理界面
首先,讓我們在 equipment/admin.py
文件中註冊我們的模型。如果這個文件不存在,請創建它。
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import User, Equipment, AdditionalEquipment, Reservation
class CustomUserAdmin(UserAdmin):
list_display = ('username', 'email', 'role', 'is_staff', 'is_active')
list_filter = ('role', 'is_staff', 'is_active')
fieldsets = (
(None, {'fields': ('username', 'email', 'password')}),
('個人信息', {'fields': ('role',)}),
('權限', {'fields': ('is_active', 'is_staff', 'is_superuser', 'groups', 'user_permissions')}),
)
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('username', 'email', 'password1', 'password2', 'role', 'is_staff', 'is_active')}
),
)
search_fields = ('username', 'email')
ordering = ('username',)
class EquipmentAdmin(admin.ModelAdmin):
list_display = ('name', 'model', 'serial_number', 'status')
list_filter = ('status',)
search_fields = ('name', 'model', 'serial_number')
class AdditionalEquipmentAdmin(admin.ModelAdmin):
list_display = ('name', 'equipment')
search_fields = ('name', 'equipment__name')
class ReservationAdmin(admin.ModelAdmin):
list_display = ('user', 'equipment', 'start_time', 'end_time', 'status')
list_filter = ('status',)
search_fields = ('user__username', 'equipment__name')
admin.site.register(User, CustomUserAdmin)
admin.site.register(Equipment, EquipmentAdmin)
admin.site.register(AdditionalEquipment, AdditionalEquipmentAdmin)
admin.site.register(Reservation, ReservationAdmin)
這段代碼為每個模型創建了自定義的管理界面,包括列表顯示、過濾器和搜索字段。
步驟 2: 自定義管理界面首頁
讓我們自定義管理界面的首頁,以顯示一些有用的統計信息。在 equipment/admin.py
中添加以下代碼:
from django.core.cache import cache
from django.db.models import Count
from django.utils.translation import gettext_lazy as _
class CustomAdminSite(admin.AdminSite):
site_header = _("實驗室設備管理系統")
site_title = _("實驗室設備管理")
index_title = _("歡迎來到實驗室設備管理系統")
def index(self, request, extra_context=None):
# 緩存統計數據,每5分鐘更新一次
stats = cache.get('admin_stats')
if not stats:
stats = {
'total_users': User.objects.count(),
'total_equipment': Equipment.objects.count(),
'total_reservations': Reservation.objects.count(),
'equipment_status': dict(Equipment.objects.values_list('status').annotate(count=Count('status'))),
'reservation_status': dict(Reservation.objects.values_list('status').annotate(count=Count('status'))),
}
cache.set('admin_stats', stats, 300) # 緩存5分鐘
extra_context = extra_context or {}
extra_context.update(stats)
return super().index(request, extra_context)
admin_site = CustomAdminSite(name='custom_admin')
# 重新註冊所有模型到自定義管理站點
admin_site.register(User, CustomUserAdmin)
admin_site.register(Equipment, EquipmentAdmin)
admin_site.register(AdditionalEquipment, AdditionalEquipmentAdmin)
admin_site.register(Reservation, ReservationAdmin)
步驟 3: 創建自定義管理界面模板
在 equipment/templates/admin/
目錄下創建一個新的 index.html
文件:
{% extends "admin/index.html" %}
{% load i18n %}
{% block content %}
<div class="row">
<div class="col-md-6">
<h2>{% trans '系統概況' %}</h2>
<table class="table">
<tr>
<th>{% trans '總用戶數' %}</th>
<td>{{ total_users }}</td>
</tr>
<tr>
<th>{% trans '總設備數' %}</th>
<td>{{ total_equipment }}</td>
</tr>
<tr>
<th>{% trans '總預約數' %}</th>
<td>{{ total_reservations }}</td>
</tr>
</table>
</div>
<div class="col-md-6">
<h2>{% trans '設備狀態' %}</h2>
<table class="table">
{% for status, count in equipment_status.items %}
<tr>
<th>{{ status }}</th>
<td>{{ count }}</td>
</tr>
{% endfor %}
</table>
</div>
</div>
<div class="row">
<div class="col-md-6">
<h2>{% trans '預約狀態' %}</h2>
<table class="table">
{% for status, count in reservation_status.items %}
<tr>
<th>{{ status }}</th>
<td>{{ count }}</td>
</tr>
{% endfor %}
</table>
</div>
</div>
{{ block.super }}
{% endblock %}
步驟 4: 更新 URL 配置
在 lab_management/urls.py
中,更新 URL 配置以使用自定義管理站點:
from django.urls import path, include
from equipment.admin import admin_site
urlpatterns = [
path('admin/', admin_site.urls),
path('', include('equipment.urls')),
]
步驟 5: 添加權限控制
在 equipment/models.py
中,為每個模型添加權限:
class Meta:
permissions = [
("can_view_all_reservations", "Can view all reservations"),
("can_manage_equipment", "Can manage equipment"),
]
這些權限可以在管理界面中分配給用戶或組。 下面為整合後版本程式碼
#equipment>models.py
from django.db import models
from django.contrib.auth.models import AbstractUser
from django.core.exceptions import ValidationError
from django.utils import timezone
class User(AbstractUser):
ROLE_CHOICES = (
('admin', '管理員'),
('user', '一般使用者'),
)
role = models.CharField(max_length=10, choices=ROLE_CHOICES, default='user')
def __str__(self):
return f"{self.username} ({self.get_role_display()})"
class Equipment(models.Model):
name = models.CharField(max_length=100, verbose_name="設備名稱")
description = models.TextField(blank=True, null=True, verbose_name="設備描述")
is_available = models.BooleanField(default=True, verbose_name="是否可用")
def __str__(self):
return self.name
class Meta:
verbose_name = "附加設備"
verbose_name_plural = "附加設備"
permissions = [
("can_view_all_reservations", "Can view all reservations"),
("can_manage_equipment", "Can manage equipment"),
]
class AdditionalEquipment(models.Model):
name = models.CharField(max_length=50, verbose_name="附加設備名稱")
def __str__(self):
return self.name
class Meta:
verbose_name = "附加設備"
verbose_name_plural = "附加設備"
permissions = [
("can_view_all_reservations", "Can view all reservations"),
("can_manage_equipment", "Can manage equipment"),
]
class Reservation(models.Model):
equipment = models.ForeignKey(Equipment, on_delete=models.CASCADE, verbose_name="主要設備")
user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="預約用戶")
test_name = models.CharField(max_length=100, verbose_name="測試名稱")
temperature = models.FloatField(verbose_name="溫度")
start_time = models.DateTimeField(verbose_name="開始時間")
end_time = models.DateTimeField(verbose_name="結束時間")
additional_equipment = models.ManyToManyField(AdditionalEquipment, blank=True, verbose_name="附加設備")
notified = models.BooleanField(default=False, verbose_name="是否已通知")
def __str__(self):
return f"{self.equipment.name} - {self.test_name} by {self.user.username}"
def clean(self):
if self.start_time >= self.end_time:
raise ValidationError("結束時間必須晚於開始時間")
# 檢查時間衝突
overlapping_reservations = Reservation.objects.filter(
equipment=self.equipment,
start_time__lt=self.end_time,
end_time__gt=self.start_time
).exclude(pk=self.pk)
if overlapping_reservations.exists():
raise ValidationError("此時間段已被預約")
class Meta:
verbose_name = "附加設備"
verbose_name_plural = "附加設備"
permissions = [
("can_view_all_reservations", "Can view all reservations"),
("can_manage_equipment", "Can manage equipment"),
]
步驟 6: 自定義管理操作
在 equipment/admin.py
中,為 EquipmentAdmin
和 ReservationAdmin
添加自定義操作:
下面程式有經過事後修改,原始版本是有問題,不能被執行
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import User, Equipment, AdditionalEquipment, Reservation
from django.core.cache import cache
from django.db.models import Count
from django.utils.translation import gettext_lazy as _
from django.http import HttpResponseRedirect
from django.urls import path
class CustomUserAdmin(UserAdmin):
list_display = ('username', 'email', 'role', 'is_staff', 'is_active')
list_filter = ('role', 'is_staff', 'is_active')
fieldsets = (
(None, {'fields': ('username', 'email', 'password')}),
('個人信息', {'fields': ('role',)}),
('權限', {'fields': ('is_active', 'is_staff', 'is_superuser', 'groups', 'user_permissions')}),
)
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('username', 'email', 'password1', 'password2', 'role', 'is_staff', 'is_active')}
),
)
search_fields = ('username', 'email')
ordering = ('username',)
class EquipmentAdmin(admin.ModelAdmin):
list_display = ('name', 'description', 'is_available')
list_filter = ('is_available',)
search_fields = ('name', 'description')
actions = ['mark_as_unavailable']
def mark_as_unavailable(self, request, queryset):
queryset.update(is_available=False)
mark_as_unavailable.short_description = "將選中的設備標記為不可用"
class AdditionalEquipmentAdmin(admin.ModelAdmin):
list_display = ('name',)
search_fields = ('name',)
class ReservationAdmin(admin.ModelAdmin):
list_display = ('user', 'equipment', 'test_name', 'temperature', 'start_time', 'end_time', 'notified')
list_filter = ('notified',)
search_fields = ('user__username', 'equipment__name', 'test_name')
filter_horizontal = ('additional_equipment',)
actions = ['mark_as_notified']
def mark_as_notified(self, request, queryset):
queryset.update(notified=True)
mark_as_notified.short_description = "將選中的預約標記為已通知"
def get_urls(self):
urls = super().get_urls()
custom_urls = [
path('notify/<int:reservation_id>/', self.admin_site.admin_view(self.notify_reservation), name='notify_reservation'),
]
return custom_urls + urls
def notify_reservation(self, request, reservation_id):
reservation = Reservation.objects.get(id=reservation_id)
reservation.notified = True
reservation.save()
self.message_user(request, f"預約 {reservation_id} 已標記為已通知")
return HttpResponseRedirect("../")
def get_queryset(self, request):
qs = super().get_queryset(request)
if request.user.is_superuser or request.user.has_perm('equipment.can_view_all_reservations'):
return qs
return qs.filter(user=request.user)
class CustomAdminSite(admin.AdminSite):
site_header = _("實驗室設備管理系統")
site_title = _("實驗室設備管理")
index_title = _("歡迎來到實驗室設備管理系統")
def index(self, request, extra_context=None):
# 緩存統計數據,每5分鐘更新一次
stats = cache.get('admin_stats')
if not stats:
stats = {
'total_users': User.objects.count(),
'total_equipment': Equipment.objects.count(),
'total_reservations': Reservation.objects.count(),
'equipment_available': dict(Equipment.objects.values_list('is_available').annotate(count=Count('is_available'))),
'reservation_notified': dict(Reservation.objects.values_list('notified').annotate(count=Count('notified'))),
}
cache.set('admin_stats', stats, 300) # 緩存5分鐘
extra_context = extra_context or {}
extra_context.update(stats)
return super().index(request, extra_context)
admin_site = CustomAdminSite(name='custom_admin')
# 重新註冊所有模型到自定義管理站點
admin_site.register(User, CustomUserAdmin)
admin_site.register(Equipment, EquipmentAdmin)
admin_site.register(AdditionalEquipment, AdditionalEquipmentAdmin)
admin_site.register(Reservation, ReservationAdmin)
這些自定義操作允許管理員快速更改設備狀態和批准/拒絕預約。
現在,您的管理員界面應該具有以下功能:
- 自定義的首頁,顯示系統概況和統計信息
- 為每個模型自定義的列表顯示和過濾器
- 用戶管理,包括角色分配
- 設備管理,包括狀態更新
- 預約管理,包括批准和拒絕功能
- 自定義權限控制
要訪問管理界面,請確保您已經創建了一個超級用戶,然後訪問 http://127.0.0.1:8000/admin/
這個管理界面為實驗室管理員提供了一個強大的工具來管理用戶、設備和預約。您可以根據需要進一步自定義和擴展這個界面。
是否還有其他方面的管理功能您想要添加或改進?
好的,我們來開發”設備列表和詳情頁面”。這將涉及到創建視圖、模板和URL配置。我們將分步驟來完成這個任務。
- 首先,讓我們在
views.py
中創建所需的視圖:
# equipment/views.py
from django.shortcuts import render, get_object_or_404
from django.views.generic import ListView, DetailView
from .models import Equipment
class EquipmentListView(ListView):
model = Equipment
template_name = 'equipment/equipment_list.html'
context_object_name = 'equipments'
paginate_by = 10 # 每頁顯示10個設備
class EquipmentDetailView(DetailView):
model = Equipment
template_name = 'equipment/equipment_detail.html'
context_object_name = 'equipment'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# 這裡可以添加額外的上下文數據,例如相關的預約信息
context['reservations'] = self.object.reservation_set.all()[:5] # 顯示最近的5個預約
return context
- 接下來,創建相應的模板文件:
<!-- equipment/templates/equipment/equipment_list.html -->
{% extends 'base.html' %}
{% block content %}
<h1>設備列表</h1>
<div class="equipment-list">
{% for equipment in equipments %}
<div class="equipment-item">
<h2><a href="{% url 'equipment_detail' equipment.pk %}">{{ equipment.name }}</a></h2>
<p>{{ equipment.description|truncatewords:20 }}</p>
<p>狀態: {% if equipment.is_available %}可用{% else %}不可用{% endif %}</p>
</div>
{% empty %}
<p>目前沒有可用的設備。</p>
{% endfor %}
</div>
{% if is_paginated %}
<div class="pagination">
<span class="step-links">
{% if page_obj.has_previous %}
<a href="?page=1">« 首頁</a>
<a href="?page={{ page_obj.previous_page_number }}">上一頁</a>
{% endif %}
<span class="current">
第 {{ page_obj.number }} 頁,共 {{ page_obj.paginator.num_pages }} 頁。
</span>
{% if page_obj.has_next %}
<a href="?page={{ page_obj.next_page_number }}">下一頁</a>
<a href="?page={{ page_obj.paginator.num_pages }}">尾頁 »</a>
{% endif %}
</span>
</div>
{% endif %}
{% endblock %}
<!-- equipment/templates/equipment/equipment_detail.html -->
{% extends 'base.html' %}
{% block content %}
<h1>{{ equipment.name }}</h1>
<div class="equipment-detail">
<p><strong>描述:</strong> {{ equipment.description }}</p>
<p><strong>狀態:</strong> {% if equipment.is_available %}可用{% else %}不可用{% endif %}</p>
</div>
<h2>最近的預約</h2>
<div class="recent-reservations">
{% for reservation in reservations %}
<div class="reservation-item">
<p><strong>用戶:</strong> {{ reservation.user.username }}</p>
<p><strong>測試名稱:</strong> {{ reservation.test_name }}</p>
<p><strong>開始時間:</strong> {{ reservation.start_time }}</p>
<p><strong>結束時間:</strong> {{ reservation.end_time }}</p>
</div>
{% empty %}
<p>目前沒有預約。</p>
{% endfor %}
</div>
{% endblock %}
- 更新
urls.py
文件以包含新的視圖:
# equipment/urls.py
from django.urls import path
from .views import EquipmentListView, EquipmentDetailView
urlpatterns = [
path('equipment/', EquipmentListView.as_view(), name='equipment_list'),
path('equipment/<int:pk>/', EquipmentDetailView.as_view(), name='equipment_detail'),
# ... 其他 URL 配置 ...
]
- 確保在主
urls.py
文件中包含了 equipment 應用的 URLs:
# project/urls.py
from django.contrib import admin
from django.urls import path, include
from equipment.admin import admin_site
urlpatterns = [
path('admin/', admin_site.urls),
path('', include('equipment.urls')),
]
- 最後,你可能需要創建一個基礎模板
base.html
,如果還沒有的話:
<!-- templates/base.html -->
<!DOCTYPE html>
<html lang="zh-Hant">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>實驗室設備管理系統</title>
<!-- 這裡可以添加 CSS 文件鏈接 -->
</head>
<body>
<header>
<!-- 這裡可以添加導航欄 -->
</header>
<main>
{% block content %}
{% endblock %}
</main>
<footer>
<!-- 這裡可以添加頁腳信息 -->
</footer>
<!-- 這裡可以添加 JavaScript 文件鏈接 -->
</body>
</html>
這樣,你就完成了設備列表和詳情頁面的基本開發。用戶可以通過訪問 /equipment/
來查看設備列表,並通過點擊設備名稱來查看詳細信息。
要使這些更改生效,請確保:
- 所有的模板文件都放在正確的目錄中。
- 你的
TEMPLATES
設置在settings.py
中正確配置。 - 運行
python manage.py collectstatic
(如果你使用靜態文件)。
如果你想添加更多功能或者美化頁面,我們可以進一步討論。例如,我們可以添加搜索功能、過濾器,或者改進頁面的樣式。
好的,讓我們來開發預約功能。這個功能將允許用戶為特定的設備創建預約。我們將分步驟來完成這個任務:
- 首先,讓我們在
views.py
中添加預約相關的視圖:
**
# equipment/views.py
from django.views.generic import ListView, DetailView, CreateView
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth import login, authenticate, logout
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from .forms import CustomUserCreationForm, CustomAuthenticationForm, ReservationForm
from .models import User, Equipment, Reservation
from django.contrib.auth.mixins import LoginRequiredMixin
from django.urls import reverse_lazy
def register_view(request):
if request.method == "POST":
form = CustomUserCreationForm(request.POST)
if form.is_valid():
user = form.save()
login(request, user)
messages.success(request, "註冊成功!")
return redirect('profile')
else:
form = CustomUserCreationForm()
return render(request, 'equipment/register.html', {'form': form})
def login_view(request):
if request.method == "POST":
form = CustomAuthenticationForm(request, data=request.POST)
if form.is_valid():
username = form.cleaned_data.get('username')
password = form.cleaned_data.get('password')
user = authenticate(username=username, password=password)
if user is not None:
login(request, user)
messages.info(request, f"您已登入為 {username}。")
return redirect('profile')
else:
messages.error(request, "無效的用戶名或密碼。")
else:
messages.error(request, "無效的用戶名或密碼。")
form = CustomAuthenticationForm()
return render(request, 'equipment/login.html', {'form': form})
@login_required
def profile_view(request):
return render(request, 'equipment/profile.html', {'user': request.user})
def logout_view(request):
logout(request)
messages.info(request, "您已成功登出。")
return redirect('login')
class EquipmentListView(ListView):
model = Equipment
template_name = 'equipment/equipment_list.html'
context_object_name = 'equipments'
paginate_by = 10 # 每頁顯示10個設備
class EquipmentDetailView(DetailView):
model = Equipment
template_name = 'equipment/equipment_detail.html'
context_object_name = 'equipment'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# 這裡可以添加額外的上下文數據,例如相關的預約信息
context['reservations'] = self.object.reservation_set.all()[:5] # 顯示最近的5個預約
return context
class ReservationCreateView(LoginRequiredMixin, CreateView):
model = Reservation
form_class = ReservationForm
template_name = 'equipment/reservation_form.html'
success_url = reverse_lazy('equipment_list')
def form_valid(self, form):
form.instance.user = self.request.user
form.instance.equipment = get_object_or_404(Equipment, pk=self.kwargs['equipment_pk'])
messages.success(self.request, '預約成功創建!')
return super().form_valid(form)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['equipment'] = get_object_or_404(Equipment, pk=self.kwargs['equipment_pk'])
return context
- 創建一個表單類
ReservationForm
:
# equipment/forms.py
from django import forms
from .models import Reservation
class ReservationForm(forms.ModelForm):
class Meta:
model = Reservation
fields = ['test_name', 'temperature', 'start_time', 'end_time', 'additional_equipment']
widgets = {
'start_time': forms.DateTimeInput(attrs={'type': 'datetime-local'}),
'end_time': forms.DateTimeInput(attrs={'type': 'datetime-local'}),
}
- 更新
urls.py
以包含新的預約創建視圖:
# equipment/urls.py
from django.urls import path
from .views import EquipmentListView, EquipmentDetailView, ReservationCreateView
urlpatterns = [
path('equipment/', EquipmentListView.as_view(), name='equipment_list'),
path('equipment/<int:pk>/', EquipmentDetailView.as_view(), name='equipment_detail'),
path('equipment/<int:equipment_pk>/reserve/', ReservationCreateView.as_view(), name='create_reservation'),
]
- 創建預約表單的模板:
<!-- equipment/templates/equipment/reservation_form.html -->
{% extends 'base.html' %}
{% block content %}
<h1>為 {{ equipment.name }} 創建預約</h1>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">創建預約</button>
</form>
{% endblock %}
- 在設備詳情頁面添加預約按鈕:
<!-- equipment/templates/equipment/equipment_detail.html -->
{% extends 'base.html' %}
{% block content %}
<h1>{{ equipment.name }}</h1>
<div class="equipment-detail">
<p><strong>描述:</strong> {{ equipment.description }}</p>
<p><strong>狀態:</strong> {% if equipment.is_available %}可用{% else %}不可用{% endif %}</p>
</div>
{% if equipment.is_available %}
<a href="{% url 'create_reservation' equipment.pk %}" class="btn btn-primary">預約此設備</a>
{% endif %}
<h2>最近的預約</h2>
<div class="recent-reservations">
{% for reservation in reservations %}
<div class="reservation-item">
<p><strong>用戶:</strong> {{ reservation.user.username }}</p>
<p><strong>測試名稱:</strong> {{ reservation.test_name }}</p>
<p><strong>開始時間:</strong> {{ reservation.start_time }}</p>
<p><strong>結束時間:</strong> {{ reservation.end_time }}</p>
</div>
{% empty %}
<p>目前沒有預約。</p>
{% endfor %}
</div>
{% endblock %}
- 在
settings.py
中設置消息框架:
# lab_management/settings.py
# ... 其他設置 ...
INSTALLED_APPS = [
# ... 其他應用 ...
'django.contrib.messages',
]
MIDDLEWARE = [
# ... 其他中間件 ...
'django.contrib.messages.middleware.MessageMiddleware',
]
# ... 其他設置 ...
- 在基礎模板中添加消息顯示:
<!-- templates/base.html -->
<!DOCTYPE html>
<html lang="zh-Hant">
<head>
<!-- ... -->
</head>
<body>
<header>
<!-- ... -->
</header>
{% if messages %}
<div class="messages">
{% for message in messages %}
<div class="alert alert-{{ message.tags }}">
{{ message }}
</div>
{% endfor %}
</div>
{% endif %}
<main>
{% block content %}
{% endblock %}
</main>
<footer>
<!-- ... -->
</footer>
</body>
</html>
這樣,我們就完成了基本的預約功能開發。用戶可以在設備詳情頁面上點擊”預約此設備”按鈕來創建預約。
為了進一步完善這個功能,你可能還需要考慮以下幾點:
- 添加預約時間衝突檢查,確保同一設備在同一時間段內不會被重複預約。
- 實現預約編輯和取消功能。
- 為管理員添加審核預約的功能。
- 實現預約提醒功能,例如在預約開始前發送郵件通知。
如果你想實現這些額外功能或者有其他需求,請告訴我,我會幫你進一步完善這個預約系統。
設定網站根URL(”/”)定義。
- 首先,我們需要為首頁創建一個視圖。在您的
equipment
應用程序的views.py
文件中添加以下代碼:
from django.shortcuts import render
def home(request):
return render(request, 'equipment/home.html')
- 然後,在
equipment
文件夾中的templates/equipment
目錄下創建一個新的home.html
文件:
{% extends 'base.html' %}
{% block content %}
<h1>歡迎來到實驗室設備管理系統</h1>
<p>這裡是您的實驗室設備管理系統的首頁。您可以在這裡查看和管理設備、預約等。</p>
{% endblock %}
- 接下來,我們需要在主
urls.py
文件中添加首頁的 URL 配置。打開您項目根目錄下的lab_management/urls.py
文件(假設您的項目名為lab_management
),並進行以下修改:
from django.urls import path, include
from equipment.admin import admin_site
from equipment import views # 導入 views
urlpatterns = [
path('', views.home, name='home'), # 添加這行作為首頁
path('admin/', admin_site.urls),
path('equipment/', include('equipment.urls')),
# 其他現有的 URL 配置...
]
- 如果您想在管理界面的 “VIEW SITE” 按鈕中使用自定義的首頁,您需要在
equipment/admin.py
文件中的CustomAdminSite
類中添加以下方法:
from django.urls import reverse
class CustomAdminSite(admin.AdminSite):
# ... 現有的代碼 ...
def get_app_list(self, request):
app_list = super().get_app_list(request)
app_list += [
{
'name': 'View Site',
'app_label': 'view_site',
'models': [{
'name': 'View Site',
'object_name': 'view_site',
'admin_url': reverse('home'),
'view_only': True,
}],
}
]
return app_list
- 最後,重新啟動您的 Django 開發服務器:
python manage.py runserver
現在,當您點擊 “VIEW SITE” 按鈕時,它應該會將您帶到新創建的首頁,而不是顯示 404 錯誤。
這些更改應該解決您的問題。現在,您的網站應該有一個有效的首頁,並且 “VIEW SITE” 按鈕應該可以正常工作。