Triển khai Quản lý Phòng Ban trong Django bằng Các View Lớp Tích Hợp

Django cung cấp một bộ lớp view sẵn có nhằm đơn giản hóa việc xử lý các thao tác CRUD (Tạo, Đọc, Cập nhật, Xóa) trên mô hình dữ liệu. Thay vì viết logic xử lý request/response từ đầu, nhà phát triển có thể kế thừa và tùy chỉnh các lớp như ListView, DetailView, CreateView, UpdateView, và DeleteView — tất cả đều nằm trong django.views.generic.

Mỗi lớp view này đã được thiết kế với hành vi mặc định hợp lý: tự động truy vấn dữ liệu, truyền ngữ cảnh vào template, xử lý form, xác thực đầu vào, và điều hướng sau khi hoàn thành. Việc mở rộng chỉ cần ghi đè các thuộc tính hoặc phương thức cụ thể như model, template_name, fields, success_url, hoặc form_valid().

1. Các lớp view được triển khai

from django.contrib.auth.mixins import LoginRequiredMixin
from django.urls import reverse_lazy
from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView

from base.models import Unit  # Đổi tên model thành Unit để phản ánh cấu trúc mới


class UnitCatalogView(LoginRequiredMixin, ListView):
    model = Unit
    template_name = "base/units/catalog.html"
    context_object_name = "unit_list"
    ordering = ["name"]


class UnitProfileView(LoginRequiredMixin, DetailView):
    model = Unit
    template_name = "base/units/profile.html"
    context_object_name = "unit"


class UnitCreationView(LoginRequiredMixin, CreateView):
    model = Unit
    fields = ["name", "supervisor", "description", "order_index", "head", "contact_number", "work_email", "is_active"]
    template_name = "base/units/form.html"
    success_url = reverse_lazy("unit_catalog")

    def form_valid(self, form):
        form.instance.creator = self.request.user
        form.instance.last_editor = self.request.user
        return super().form_valid(form)

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["page_header"] = "Tạo phòng ban mới"
        context["submit_label"] = "Lưu lại"
        context["available_parents"] = Unit.objects.all()
        return context


class UnitModificationView(LoginRequiredMixin, UpdateView):
    model = Unit
    fields = ["name", "supervisor", "description", "order_index", "head", "contact_number", "work_email", "is_active"]
    template_name = "base/units/form.html"
    success_url = reverse_lazy("unit_catalog")

    def form_valid(self, form):
        form.instance.last_editor = self.request.user
        return super().form_valid(form)

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["page_header"] = f"Chỉnh sửa '{self.object.name}'"
        context["submit_label"] = "Cập nhật"
        context["available_parents"] = Unit.objects.exclude(id=self.object.id)
        return context


class UnitRemovalView(LoginRequiredMixin, DeleteView):
    model = Unit
    template_name = "base/units/confirm_delete.html"
    success_url = reverse_lazy("unit_catalog")

2. Định tuyến URL

from django.urls import path
from .views import (
    UnitCatalogView,
    UnitProfileView,
    UnitCreationView,
    UnitModificationView,
    UnitRemovalView,
)

app_name = "units"

urlpatterns = [
    path("", UnitCatalogView.as_view(), name="unit_catalog"),
    path("/", UnitProfileView.as_view(), name="unit_profile"),
    path("new/", UnitCreationView.as_view(), name="unit_create"),
    path("/edit/", UnitModificationView.as_view(), name="unit_update"),
    path("/delete/", UnitRemovalView.as_view(), name="unit_delete"),
]

3. Mẫu HTML minh họa

catalog.html — Danh sách phòng ban với nút thao tác:

{% extends "layout/base.html" %}

{% block content %}
  <div class="mb-3">
    <a href="{% url 'units:unit_create' %}" class="btn btn-outline-primary btn-sm">+ Thêm phòng ban</a>
  </div>

  <div class="card">
    <div class="card-header bg-light">
      <h5 class="mb-0">Danh mục phòng ban</h5>
    </div>
    <div class="card-body p-0">
      <table class="table table-hover mb-0">
        <thead class="table-light">
          <tr>
            <th>ID</th>
            <th>Tên phòng ban</th>
            <th>Phòng ban cha</th>
            <th>Thứ tự hiển thị</th>
            <th>Người phụ trách</th>
            <th>Trạng thái</th>
            <th>Hành động</th>
          </tr>
        </thead>
        <tbody>
          {% for unit in unit_list %}
            <tr>
              <td>{{ unit.id }}</td>
              <td>{{ unit.name }}</td>
              <td>{{ unit.supervisor|default:"–" }}</td>
              <td>{{ unit.order_index|default:"0" }}</td>
              <td>{{ unit.head|default:"Chưa chỉ định" }}</td>
              <td>
                <span class="badge bg-{% if unit.is_active %}success{% else %}secondary{% endif %}">
                  {{ unit.get_is_active_display }}
                </span>
              </td>
              <td>
                <a href="{% url 'units:unit_profile' unit.pk %}" class="btn btn-sm btn-info">Xem</a>
                <a href="{% url 'units:unit_update' unit.pk %}" class="btn btn-sm btn-warning">Sửa</a>
                <a href="{% url 'units:unit_delete' unit.pk %}" class="btn btn-sm btn-danger">Xóa</a>
              </td>
            </tr>
          {% empty %}
            <tr>
              <td colspan="7" class="text-center text-muted py-4">Chưa có phòng ban nào.</td>
            </tr>
          {% endfor %}
        </tbody>
      </table>
    </div>
  </div>
{% endblock %}

form.html — Form chung cho tạo và cập nhật:

{% extends "layout/base.html" %}
{% load widget_tweaks %}

{% block content %}
  <div class="container mt-4">
    <div class="card">
      <div class="card-header bg-white">
        <h5 class="mb-0">{{ page_header }}</h5>
      </div>
      <div class="card-body">
        <form method="post">
          {% csrf_token %}
          {% for field in form %}
            <div class="mb-3">
              <label class="form-label">{{ field.label }}</label>
              {{ field|add_class:"form-control" }}
              {% if field.errors %}
                <div class="text-danger small">{{ field.errors }}</div>
              {% endif %}
            </div>
          {% endfor %}
          <div class="d-flex gap-2">
            <button type="submit" class="btn btn-primary">{{ submit_label }}</button>
            <a href="{% url 'units:unit_catalog' %}" class="btn btn-secondary">Hủy</a>
          </div>
        </form>
      </div>
    </div>
  </div>
{% endblock %}

confirm_delete.html — Xác nhận xóa an toàn:

{% extends "layout/base.html" %}

{% block content %}
  <div class="container mt-4">
    <div class="card">
      <div class="card-header bg-danger text-white">
        <h5 class="mb-0">Xác nhận xóa</h5>
      </div>
      <div class="card-body">
        <p>Bạn có chắc chắn muốn xóa phòng ban <strong>"{{ unit.name }}"</strong> không?</p>
        <p class="text-danger fw-bold">Hành động này không thể hoàn tác.</p>
        <form method="post">
          {% csrf_token %}
          <button type="submit" class="btn btn-danger">Đồng ý xóa</button>
          <a href="{% url 'units:unit_catalog' %}" class="btn btn-secondary">Hủy</a>
        </form>
      </div>
    </div>
  </div>
{% endblock %}

Thẻ: Django cbv Generics view-class CRUD

Đăng vào ngày 8 tháng 6 lúc 17:49