Python 使用Claude 3.5 Sonnet建置設備管理系統

此次專案是練習使用Claude 3.5 Sonnet建置設備管理系統,因此用這篇文章紀錄整個過程

  1. 用戶認證系統
  2. 設備列表和詳情頁面
  3. 預約功能
  4. 管理員界面

非常好,我很高興能夠帶領您開始開發這個實驗室設備管理系統。我們將按照軟件開發的標準流程來進行,從環境設置開始,然後逐步實現核心功能。讓我們開始吧!

步驟 1: 設置開發環境

首先,我們需要設置我們的開發環境。我們將使用 Python 和 Django 來開發這個系統。

  1. 安裝 Python(如果尚未安裝)
  2. 創建虛擬環境
  3. 安裝 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

這些步驟將為我們的項目建立基本的結構和數據模型。接下來,我們將開始實現視圖和模板,以及用戶認證系統。

您希望我們接下來關注哪個具體功能的實現?例如:

  1. 用戶認證系統
  2. 設備列表和詳情頁面
  3. 預約功能
  4. 管理員界面

請告訴我您想優先實現哪個功能,我們將繼續開發過程。


好的,讓我們來實現用戶認證系統。我們將使用 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 來測試用戶認證系統:

這樣,我們就完成了基本的用戶認證系統。您可以註冊新用戶,登入,查看個人資料,以及登出。系統還包括了基本的錯誤處理和消息顯示。

接下來,我們可以繼續完善系統,例如添加密碼重置功能,或者開始實現設備管理和預約系統的功能。您希望下一步做什麼?


好的,讓我們來開發管理員界面。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 中,為 EquipmentAdminReservationAdmin 添加自定義操作:

下面程式有經過事後修改,原始版本是有問題,不能被執行

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)

這些自定義操作允許管理員快速更改設備狀態和批准/拒絕預約。

現在,您的管理員界面應該具有以下功能:

  1. 自定義的首頁,顯示系統概況和統計信息
  2. 為每個模型自定義的列表顯示和過濾器
  3. 用戶管理,包括角色分配
  4. 設備管理,包括狀態更新
  5. 預約管理,包括批准和拒絕功能
  6. 自定義權限控制

要訪問管理界面,請確保您已經創建了一個超級用戶,然後訪問 http://127.0.0.1:8000/admin/

這個管理界面為實驗室管理員提供了一個強大的工具來管理用戶、設備和預約。您可以根據需要進一步自定義和擴展這個界面。

是否還有其他方面的管理功能您想要添加或改進?


好的,我們來開發”設備列表和詳情頁面”。這將涉及到創建視圖、模板和URL配置。我們將分步驟來完成這個任務。

  1. 首先,讓我們在 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

  1. 接下來,創建相應的模板文件:
<!-- 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">&laquo; 首頁</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 }}">尾頁 &raquo;</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 %}

  1. 更新 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 配置 ...
]

  1. 確保在主 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')),
]

  1. 最後,你可能需要創建一個基礎模板 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/ 來查看設備列表,並通過點擊設備名稱來查看詳細信息。

要使這些更改生效,請確保:

  1. 所有的模板文件都放在正確的目錄中。
  2. 你的 TEMPLATES 設置在 settings.py 中正確配置。
  3. 運行 python manage.py collectstatic(如果你使用靜態文件)。

如果你想添加更多功能或者美化頁面,我們可以進一步討論。例如,我們可以添加搜索功能、過濾器,或者改進頁面的樣式。


好的,讓我們來開發預約功能。這個功能將允許用戶為特定的設備創建預約。我們將分步驟來完成這個任務:

  1. 首先,讓我們在 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
  1. 創建一個表單類 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'}),
        }

  1. 更新 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'),
]

  1. 創建預約表單的模板:
<!-- 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 %}

  1. 在設備詳情頁面添加預約按鈕:
<!-- 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 %}

  1. settings.py 中設置消息框架:
# lab_management/settings.py

# ... 其他設置 ...

INSTALLED_APPS = [
    # ... 其他應用 ...
    'django.contrib.messages',
]

MIDDLEWARE = [
    # ... 其他中間件 ...
    'django.contrib.messages.middleware.MessageMiddleware',
]

# ... 其他設置 ...

  1. 在基礎模板中添加消息顯示:
<!-- 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>

這樣,我們就完成了基本的預約功能開發。用戶可以在設備詳情頁面上點擊”預約此設備”按鈕來創建預約。

為了進一步完善這個功能,你可能還需要考慮以下幾點:

  1. 添加預約時間衝突檢查,確保同一設備在同一時間段內不會被重複預約。
  2. 實現預約編輯和取消功能。
  3. 為管理員添加審核預約的功能。
  4. 實現預約提醒功能,例如在預約開始前發送郵件通知。

如果你想實現這些額外功能或者有其他需求,請告訴我,我會幫你進一步完善這個預約系統。


設定網站根URL(”/”)定義。

  1. 首先,我們需要為首頁創建一個視圖。在您的 equipment 應用程序的 views.py 文件中添加以下代碼:
from django.shortcuts import render

def home(request):
    return render(request, 'equipment/home.html')

  1. 然後,在 equipment 文件夾中的 templates/equipment 目錄下創建一個新的 home.html 文件:
{% extends 'base.html' %}

{% block content %}
<h1>歡迎來到實驗室設備管理系統</h1>
<p>這裡是您的實驗室設備管理系統的首頁。您可以在這裡查看和管理設備、預約等。</p>
{% endblock %}

  1. 接下來,我們需要在主 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 配置...
]

  1. 如果您想在管理界面的 “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

  1. 最後,重新啟動您的 Django 開發服務器:
python manage.py runserver

現在,當您點擊 “VIEW SITE” 按鈕時,它應該會將您帶到新創建的首頁,而不是顯示 404 錯誤。

這些更改應該解決您的問題。現在,您的網站應該有一個有效的首頁,並且 “VIEW SITE” 按鈕應該可以正常工作。

發表迴響