Bài viết này sẽ tổng hợp và mở rộng các kiến thức cốt lõi trong phát triển ứng dụng web với Django, bao gồm hệ thống ORM mạnh mẽ, cách thức hoạt động của Cookie và Session, kiến trúc Middleware linh hoạt, kỹ thuật AJAX để tạo trải nghiệm người dùng mượt mà, và cuối cùng là hệ thống Form Components hiệu quả.
1. Ôn Tập Các Khái Niệm Chính
Django ORM (Object-Relational Mapper)
Django ORM cho phép tương tác với cơ sở dữ liệu bằng các đối tượng Python, thay vì viết các câu lệnh SQL trực tiếp.
Các tham số quan trọng khi định nghĩa trường Model:
null=True: Cho phép trường này có giá trịNULLtrong cơ sở dữ liệu.blank=True: Cho phép trường này trống (empty) khi xác thực form.default: Thiết lập giá trị mặc định cho trường nếu không có giá trị nào được cung cấp.
Các phương thức truy vấn cơ bản:
Những phương thức này giúp bạn lấy dữ liệu một cách linh hoạt:
all(): Trả về một QuerySet chứa tất cả các đối tượng.filter(**kwargs): Trả về QuerySet chứa các đối tượng phù hợp với điều kiện.exclude(**kwargs): Trả về QuerySet chứa các đối tượng KHÔNG phù hợp với điều kiện.values(*fields): Trả về QuerySet của các dictionary, mỗi dictionary đại diện cho một đối tượng và chỉ chứa các trường được chỉ định.values_list(*fields, flat=False): Tương tự nhưvalues(), nhưng trả về QuerySet của các tuple. Nếuflat=Truevà chỉ có một trường được chọn, nó sẽ trả về một danh sách các giá trị đơn.order_by(*fields): Sắp xếp QuerySet theo các trường được chỉ định (thêm-cho thứ tự giảm dần).reverse(): Đảo ngược thứ tự của QuerySet đã được sắp xếp trước đó.distinct(*fields): Loại bỏ các hàng trùng lặp.
Đối với việc lấy một hoặc kiểm tra sự tồn tại của đối tượng:
get(**kwargs): Trả về một đối tượng duy nhất khớp với điều kiện. Sẽ gây lỗi nếu không tìm thấy hoặc tìm thấy nhiều hơn một.first(): Trả về đối tượng đầu tiên trong QuerySet, hoặcNonenếu QuerySet trống.last(): Trả về đối tượng cuối cùng trong QuerySet, hoặcNonenếu QuerySet trống.count(): Trả về số lượng đối tượng trong QuerySet.exists(): Trả vềTruenếu QuerySet chứa ít nhất một đối tượng,Falsenếu không.
Tra cứu nâng cao với dấu gạch dưới kép (__):
Django ORM cung cấp cú pháp __ để thực hiện các tra cứu phức tạp hơn:
- So sánh:
__gt(lớn hơn),__lt(nhỏ hơn),__gte(lớn hơn hoặc bằng),__lte(nhỏ hơn hoặc bằng). - Tập hợp:
__in=[](trong danh sách),__range=[](trong khoảng). - Đối sánh chuỗi:
__contains(chứa chuỗi, phân biệt chữ hoa/thường),__icontains(chứa chuỗi, không phân biệt chữ hoa/thường),__startswith,__endswith,__istartswith,__iendswith. - Kiểm tra
NULL:__isnull=True.
Quan hệ khóa ngoại (Một-nhiều):
Quan hệ ForeignKey thể hiện một đối tượng thuộc về một đối tượng khác (ví dụ: một cuốn sách có một nhà xuất bản).
from django.db import models
class NhaXuatBan(models.Model):
ten_nxb = models.CharField(max_length=100)
dia_chi = models.CharField(max_length=200)
class Sach(models.Model):
tieu_de = models.CharField(max_length=200)
gia_tien = models.DecimalField(max_digits=5, decimal_places=2)
# Định nghĩa ForeignKey đến NhaXuatBan
nha_xuat_ban = models.ForeignKey(NhaXuatBan, on_delete=models.CASCADE, related_name='sach_da_xuat_ban')
# Truy cập từ đối tượng con đến cha
mot_cuon_sach = Sach.objects.get(id=1)
print(mot_cuon_sach.nha_xuat_ban.ten_nxb) # Truy cập thuộc tính của đối tượng NhaXuatBan
# Truy cập từ đối tượng cha đến các đối tượng con (sử dụng related_name hoặc mặc định _set)
mot_nxb = NhaXuatBan.objects.get(id=1)
cac_sach_cua_nxb = mot_nxb.sach_da_xuat_ban.all()
# Truy vấn liên quan
sach_voi_nxb_cu_the = Sach.objects.filter(nha_xuat_ban__ten_nxb='NXB ABC')
nxb_voi_sach_cu_the = NhaXuatBan.objects.filter(sach_da_xuat_ban__tieu_de__icontains='Python')
Quan hệ nhiều-nhiều:
Quan hệ ManyToManyField cho phép một đối tượng có thể liên kết với nhiều đối tượng khác và ngược lại (ví dụ: một cuốn sách có nhiều tác giả, một tác giả có nhiều cuốn sách).
from django.db import models
class TacGia(models.Model):
ten_tac_gia = models.CharField(max_length=100)
class Sach(models.Model):
tieu_de = models.CharField(max_length=200)
tac_gia = models.ManyToManyField(TacGia, related_name='cac_sach_viet')
# Truy cập và quản lý quan hệ nhiều-nhiều
mot_cuon_sach = Sach.objects.get(id=1)
# Lấy tất cả tác giả của sách
tat_ca_tac_gia = mot_cuon_sach.tac_gia.all()
# Thêm tác giả
tac_gia_moi = TacGia.objects.create(ten_tac_gia='Nguyễn Nhật Ánh')
mot_cuon_sach.tac_gia.add(tac_gia_moi) # Thêm một tác giả
mot_cuon_sach.tac_gia.add(2, 3) # Thêm theo ID
# Loại bỏ tác giả
mot_cuon_sach.tac_gia.remove(tac_gia_moi) # Loại bỏ một tác giả
mot_cuon_sach.tac_gia.remove(2)
# Thiết lập lại toàn bộ (thay thế tất cả các tác giả hiện có)
tac_gia_khac = TacGia.objects.filter(id__in=[4, 5])
mot_cuon_sach.tac_gia.set(tac_gia_khac)
# Xóa tất cả các tác giả
mot_cuon_sach.tac_gia.clear()
# Tạo một tác giả mới và liên kết ngay lập tức
tac_gia_vua_tao = mot_cuon_sach.tac_gia.create(ten_tac_gia='Tác Giả Mới')
Tổng hợp (Aggregation) và Nhóm (Annotation):
Các hàm tổng hợp như Sum, Avg, Max, Min, Count cho phép tính toán trên tập hợp dữ liệu.
aggregate(): Trả về một dictionary chứa kết quả tổng hợp duy nhất cho toàn bộ QuerySet.annotate(): Thêm một trường tổng hợp vào từng đối tượng trong QuerySet, cho phép nhóm dữ liệu.
from django.db.models import Max, Min, Sum, Avg, Count
# Ví dụ Aggregate: Tìm giá sách cao nhất
ket_qua_tong_hop = Sach.objects.aggregate(gia_cao_nhat=Max('gia_tien'), so_luong_sach=Count('id'))
print(ket_qua_tong_hop) # Output: {'gia_cao_nhat': Decimal('150.00'), 'so_luong_sach': 50}
# Ví dụ Annotate: Đếm số sách và tìm giá trung bình cho mỗi nhà xuất bản
thong_ke_nxb = NhaXuatBan.objects.annotate(
tong_sach=Count('sach_da_xuat_ban'),
gia_trung_binh_sach=Avg('sach_da_xuat_ban__gia_tien')
).values('ten_nxb', 'tong_sach', 'gia_trung_binh_sach')
print(list(thong_ke_nxb))
Đối tượng F và Q:
F(): Cho phép tham chiếu đến các trường model trong truy vấn. Thích hợp để so sánh các trường với nhau hoặc thực hiện các phép toán trên các giá trị trường.Q(): Xây dựng các truy vấn phức tạp bằng cách kết hợp nhiều điều kiện sử dụng các toán tử logic (&cho AND,|cho OR,~cho NOT).
from django.db.models import F, Q
# Ví dụ F object: Tìm sản phẩm mà số lượng bán lớn hơn số lượng trong kho
models.SanPham.objects.filter(so_luong_ban__gt=F('so_luong_ton_kho'))
# Cập nhật trường dựa trên giá trị hiện có
models.SanPham.objects.update(gia_ban=F('gia_ban') * 1.05 + 10) # Tăng 5% và thêm 10 đơn vị
# Ví dụ Q object: Tìm người dùng có ID lớn hơn 10 HOẶC tên bắt đầu bằng 'admin'
models.NguoiDung.objects.filter(Q(ma_nguoi_dung__gt=10) | Q(ten_dang_nhap__startswith='admin'))
# Kết hợp với toán tử AND
models.NguoiDung.objects.filter(Q(ten_dang_nhap__startswith='test') & Q(email__isnull=False))
Giao dịch cơ sở dữ liệu (Transactions):
Django cung cấp một cách dễ dàng để quản lý các giao dịch cơ sở dữ liệu, đảm bảo tính toàn vẹn dữ liệu. Với transaction.atomic(), một khối code sẽ được thực thi như một giao dịch duy nhất: hoặc tất cả các thay đổi được cam kết, hoặc không thay đổi nào được thực hiện nếu có lỗi.
from django.db import transaction
try:
with transaction.atomic():
# Các thao tác cơ sở dữ liệu trong một khối giao dịch
NhaXuatBan.objects.create(ten_nxb='NXB Cánh Diều', dia_chi='Hà Nội')
# Giả sử có một lỗi xảy ra ở đây, toàn bộ giao dịch sẽ được rollback
# models.SomeModel.objects.create(invalid_field='...')
print("Tạo nhà xuất bản thành công.")
except Exception as e:
print(f"Đã xảy ra lỗi trong giao dịch: {e}")
# Các thay đổi sẽ không được lưu vào DB
Cookie và Session
Cookie:
Cookie là một mảnh dữ liệu nhỏ mà máy chủ gửi đến trình duyệt web của người dùng và trình duyệt sẽ lưu trữ nó. Sau đó, mỗi khi trình duyệt gửi một yêu cầu mới đến cùng máy chủ, nó sẽ gửi lại cookie đó.
- Thiết lập Cookie: Sử dụng
HttpResponse.set_cookie(key, value, max_age=None, expires=None).from django.shortcuts import HttpResponse def set_cookie_view(request): response = HttpResponse("Cookie đã được thiết lập!") response.set_cookie('ngon_ngu_uu_tien', 'vi', max_age=3600) # Hết hạn sau 1 giờ return response - Đọc Cookie: Cookie được lưu trữ trong
request.COOKIESlà một dictionary.def get_cookie_view(request): ngon_ngu = request.COOKIES.get('ngon_ngu_uu_tien', 'en') return HttpResponse(f"Ngôn ngữ ưu tiên của bạn: {ngon_ngu}") - Xóa Cookie: Sử dụng
HttpResponse.delete_cookie(key).def delete_cookie_view(request): response = HttpResponse("Cookie đã bị xóa!") response.delete_cookie('ngon_ngu_uu_tien') return response
Session:
Session lưu trữ dữ liệu trên máy chủ, liên kết với một người dùng cụ thể thông qua một session ID thường được lưu trữ trong một cookie. Session là một cách an toàn hơn để lưu trữ dữ liệu nhạy cảm hoặc lớn hơn.
- Ghi dữ liệu vào Session:
def set_session_view(request): request.session['ten_nguoi_dung'] = 'Alice' request.session['cart_items'] = [101, 203] return HttpResponse("Dữ liệu session đã được lưu.") - Đọc dữ liệu từ Session:
def get_session_view(request): ten = request.session.get('ten_nguoi_dung', 'Khách') gio_hang = request.session.get('cart_items', []) return HttpResponse(f"Xin chào, {ten}. Các mặt hàng trong giỏ: {gio_hang}") - Xóa dữ liệu Session:
def delete_session_key(request): if 'ten_nguoi_dung' in request.session: del request.session['ten_nguoi_dung'] return HttpResponse("Tên người dùng đã bị xóa khỏi session.") def clear_all_session_data(request): request.session.flush() # Xóa tất cả dữ liệu và tạo session ID mới # request.session.clear() # Xóa tất cả dữ liệu nhưng giữ session ID return HttpResponse("Toàn bộ dữ liệu session đã bị xóa.")
2. Các Chủ Đề Mới
Middleware trong Django
Middleware là một cơ chế mạnh mẽ trong Django cho phép bạn "can thiệp" vào chu trình xử lý request và response. Mỗi middleware là một lớp (class) Python thực hiện một số hành động nhất định tại các điểm cụ thể trong luồng xử lý.
Các phương thức chính của Middleware:
process_request(self, request):- Mục đích: Xử lý request ngay khi nó được nhận bởi Django, trước khi bất kỳ URL nào được phân giải.
- Tham số:
request(đối tượngHttpRequest). - Thời điểm thực thi: Rất sớm trong chu trình, trước khi tìm kiếm URL và View.
- Thứ tự thực thi: Chạy theo thứ tự chúng được liệt kê trong
MIDDLEWAREsetting. - Giá trị trả về:
None: Cho phép request tiếp tục xử lý qua các middleware tiếp theo và cuối cùng đến View.HttpResponse: Ngắt dòng chảy xử lý ngay lập tức. Các middleware sau đó (trongprocess_requestvàprocess_view), việc phân giải URL và View sẽ không được thực hiện. Django sẽ chuyển đếnprocess_responsecủa middleware hiện tại và sau đó là các middleware trước đó (theo thứ tự ngược lại).
process_response(self, request, response):- Mục đích: Xử lý response trước khi nó được gửi về client.
- Tham số:
request(đối tượngHttpRequest),response(đối tượngHttpResponse). - Thời điểm thực thi: Sau khi View đã thực thi và trả về một response, hoặc sau khi một middleware nào đó đã trả về một
HttpResponsetừprocess_request/process_view/process_exception. - Thứ tự thực thi: Chạy theo thứ tự ngược lại so với thứ tự đăng ký.
- Giá trị trả về: Bắt buộc phải trả về một đối tượng
HttpResponse.
process_view(self, request, view_func, view_args, view_kwargs):- Mục đích: Thực hiện logic ngay trước khi View được gọi.
- Tham số:
request,view_func(hàm View sẽ được gọi),view_args(tham số vị trí cho View),view_kwargs(tham số từ khóa cho View). - Thời điểm thực thi: Sau khi URL đã được phân giải, nhưng trước khi View được thực thi.
- Thứ tự thực thi: Chạy theo thứ tự đăng ký.
- Giá trị trả về:
None: View sẽ được gọi.HttpResponse: Ngắt dòng chảy xử lý. View và các middlewareprocess_viewsau đó sẽ không được gọi. Request sẽ chuyển trực tiếp đếnprocess_responsecủa các middleware đã được thực thi (theo thứ tự ngược).
process_exception(self, request, exception):- Mục đích: Xử lý các ngoại lệ phát sinh trong View hoặc trong các middleware sau đó.
- Tham số:
request,exception(đối tượng ngoại lệ). - Thời điểm thực thi: Chỉ được gọi khi có một ngoại lệ được raise bởi View hoặc middleware sau đó.
- Thứ tự thực thi: Chạy theo thứ tự ngược lại so với thứ tự đăng ký.
- Giá trị trả về:
None: Cho phép ngoại lệ tiếp tục xử lý bởi các middlewareprocess_exceptiontrước đó hoặc bởi Django.HttpResponse: Xử lý ngoại lệ và trả về một response tùy chỉnh (ví dụ: trang lỗi 500). Các middlewareprocess_exceptiontrước đó sẽ không được gọi.
process_template_response(self, request, response):- Mục đích: Điều chỉnh một
TemplateResponsetrước khi nó được render. - Tham số:
request,response(phải là một đối tượngTemplateResponse). - Thời điểm thực thi: Chỉ được gọi nếu View hoặc middleware trước đó đã trả về một
TemplateResponse(không phảiHttpResponseđã được render đầy đủ). - Thứ tự thực thi: Chạy theo thứ tự ngược lại so với thứ tự đăng ký.
- Giá trị trả về: Phải trả về một đối tượng
TemplateResponsehoặcHttpResponse.
- Mục đích: Điều chỉnh một
AJAX (Asynchronous JavaScript and XML)
AJAX là một kỹ thuật phát triển web cho phép ứng dụng web gửi và nhận dữ liệu từ server một cách không đồng bộ mà không cần tải lại toàn bộ trang. Điều này mang lại trải nghiệm người dùng mượt mà và nhanh chóng hơn.
Ưu điểm của AJAX:
- Cập nhật cục bộ: Chỉ một phần của trang được cập nhật, giảm băng thông và tăng tốc độ.
- Không đồng bộ: Người dùng có thể tiếp tục tương tác với trang trong khi dữ liệu đang được tải.
- Đa dạng dữ liệu: Thường sử dụng JSON, XML hoặc văn bản thuần túy để trao đổi dữ liệu.
Ví dụ gửi request AJAX với jQuery:
Giả sử chúng ta có một form đơn giản để tính tổng hai số:
<input type="text" name="so_hang_1" value="10"> +
<input type="text" name="so_hang_2" value="20"> =
<input type="text" name="ket_qua" readonly>
<button id="btn_thuc_hien_phep_tinh">Tính toán</button>
// JavaScript (sử dụng jQuery)
$(document).ready(function() {
$('#btn_thuc_hien_phep_tinh').on('click', function() {
$.ajax({
url: '/api/tinh-tong/', // Điểm cuối API trên server Django
type: 'POST', // Phương thức HTTP
data: { // Dữ liệu gửi đi
tham_so_a: $('input[name="so_hang_1"]').val(),
tham_so_b: $('input[name="so_hang_2"]').val(),
csrfmiddlewaretoken: $('input[name="csrfmiddlewaretoken"]').val() // Quan trọng cho POST
},
success: function (phan_hoi_server) { // Callback khi request thành công
// Giả sử server trả về JSON: { "tong": 30 }
$('input[name="ket_qua"]').val(phan_hoi_server.tong);
},
error: function (xhr, trang_thai, loi) { // Callback khi request thất bại
console.error("Lỗi khi thực hiện tính toán: " + loi);
alert("Đã xảy ra lỗi trong quá trình tính toán.");
}
});
});
});
Các tham số quan trọng của $.ajax():
url: Địa chỉ URL đích của request.type(hoặcmethod): Phương thức HTTP (GET, POST, PUT, DELETE).data: Dữ liệu gửi đi (có thể là đối tượng JavaScript hoặc chuỗi).success(data, textStatus, jqXHR): Hàm callback khi request thành công.error(jqXHR, textStatus, errorThrown): Hàm callback khi request thất bại.processData: false: Khi gửi dữ liệu không phải là query string (ví dụ: FormData cho tải file), jQuery sẽ không cố gắng chuyển đổi nó thành chuỗi query.contentType: false: Khi gửi dữ liệu không phải là query string, jQuery sẽ không tự động đặt headerContent-Type(để trình duyệt tự đặt cho FormData).
Ví dụ tải file qua AJAX:
// HTML
<input type="file" id="chon_file_tai_len">
<button id="btn_tai_len_file">Tải lên</button>
// JavaScript
$(document).ready(function() {
$('#btn_tai_len_file').on('click', function() {
var du_lieu_form = new FormData();
du_lieu_form.append('file_upload', $('#chon_file_tai_len')[0].files[0]);
du_lieu_form.append('csrfmiddlewaretoken', $('input[name="csrfmiddlewaretoken"]').val());
$.ajax({
url: '/tai-lieu/upload/',
type: 'POST',
processData: false, // Quan trọng: jQuery không xử lý dữ liệu
contentType: false, // Quan trọng: jQuery không đặt Content-Type header
data: du_lieu_form,
success: function (phan_hoi) {
alert("File đã được tải lên thành công!");
console.log(phan_hoi);
},
error: function (xhr, trang_thai, loi_err) {
alert("Lỗi khi tải file: " + loi_err);
console.error(xhr.responseText);
}
});
});
});
Bảo vệ CSRF khi gửi POST request AJAX:
Django yêu cầu token CSRF cho các request POST để ngăn chặn tấn công giả mạo yêu cầu chéo trang. Có ba cách chính để gửi token này qua AJAX:
- Thêm vào dữ liệu (data) của request: Như ví dụ trên, thêm
csrfmiddlewaretoken: $('input[name="csrfmiddlewaretoken"]').val()vào đối tượngdata. - Thêm vào header của request:
$.ajax({ url: '/some-url/', type: 'POST', data: { 'key': 'value' }, headers: { 'X-CSRFToken': $('[name="csrfmiddlewaretoken"]').val() }, // ... }); - Sử dụng hàm tiện ích của Django: Nếu bạn đang sử dụng jQuery, Django cung cấp một cách tự động để xử lý CSRF bằng cách include một script nhỏ trong template của bạn, script này sẽ tự động thêm header X-CSRFToken vào mọi request AJAX. Bạn cần đảm bảo thẻ
<script>sau được render trong template của mình.<script> // Cài đặt AJAX để bao gồm CSRF token trong header X-CSRFToken function getCookie(name) { var cookieValue = null; if (document.cookie && document.cookie !== '') { var cookies = document.cookie.split(';'); for (var i = 0; i < cookies.length; i++) { var cookie = jQuery.trim(cookies[i]); // Does this cookie string begin with the name we want? if (cookie.substring(0, name.length + 1) === (name + '=')) { cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); break; } } } return cookieValue; } var csrftoken = getCookie('csrftoken'); function csrfSafeMethod(method) { // these HTTP methods do not require CSRF protection return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); } $.ajaxSetup({ beforeSend: function(xhr, settings) { if (!csrfSafeMethod(settings.type) && !this.crossDomain) { xhr.setRequestHeader("X-CSRFToken", csrftoken); } } }); </script>
Hệ thống Form của Django
Hệ thống Form của Django giúp quản lý các biểu mẫu HTML, từ việc tạo ra chúng đến việc xác thực dữ liệu và hiển thị thông báo lỗi. Nó tách biệt logic của form khỏi View và Template.
Công dụng chính của Form Components:
- Tự động sinh HTML: Tạo các trường input và label tương ứng.
- Xác thực dữ liệu: Kiểm tra tính hợp lệ của dữ liệu người dùng nhập.
- Hiển thị thông báo lỗi: Cung cấp phản hồi rõ ràng khi dữ liệu không hợp lệ.
Ví dụ về Form đăng ký người dùng:
Đầu tiên, định nghĩa Form trong một tệp forms.py:
from django import forms
class FormDangKyNguoiDung(forms.Form):
ten_nguoi_dung = forms.CharField(
label='Tên người dùng',
min_length=6,
max_length=50,
help_text='Tên người dùng phải có ít nhất 6 ký tự.'
)
mat_khau_1 = forms.CharField(
label='Mật khẩu',
widget=forms.PasswordInput,
min_length=8
)
mat_khau_2 = forms.CharField(
label='Xác nhận mật khẩu',
widget=forms.PasswordInput
)
email = forms.EmailField(
label='Địa chỉ Email',
required=False # Không bắt buộc
)
Sử dụng Form trong View:
from django.shortcuts import render, redirect
from .forms import FormDangKyNguoiDung # Giả sử forms.py nằm cùng thư mục
def trang_dang_ky(request):
if request.method == 'POST':
form_hien_tai = FormDangKyNguoiDung(request.POST) # Liên kết dữ liệu POST với form
if form_hien_tai.is_valid():
# Dữ liệu đã hợp lệ và được làm sạch
du_lieu_da_xac_thuc = form_hien_tai.cleaned_data
# Tại đây, bạn có thể xử lý dữ liệu, ví dụ: tạo người dùng mới
print(f"Người dùng mới: {du_lieu_da_xac_thuc['ten_nguoi_dung']}")
# Chuyển hướng đến trang thành công
return redirect('/dang-ky-thanh-cong/')
else:
form_hien_tai = FormDangKyNguoiDung() # Tạo một form rỗng cho GET request
return render(request, 'trang_dang_ky.html', {'form_hien_thi': form_hien_tai})
Hiển thị Form trong Template:
<!DOCTYPE html>
<html lang="vi">
<head>
<meta charset="UTF-8">
<title>Đăng Ký Người Dùng</title>
</head>
<body>
<h1>Đăng Ký Tài Khoản Mới</h1>
<form method="post">
{% csrf_token %}
{{ form_hien_thi.as_p }} <!-- Render form theo dạng paragraph -->
<!-- Hoặc hiển thị từng trường một để tùy chỉnh -->
<!--
<p>
<label for="{{ form_hien_thi.ten_nguoi_dung.id_for_label }}">
{{ form_hien_thi.ten_nguoi_dung.label }}:
</label>
{{ form_hien_thi.ten_nguoi_dung }}
{% if form_hien_thi.ten_nguoi_dung.errors %}
<ul class="errorlist">
{% for error in form_hien_thi.ten_nguoi_dung.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
<small>{{ form_hien_thi.ten_nguoi_dung.help_text }}</small>
</p>
<p>{{ form_hien_thi.mat_khau_1.label_tag }} {{ form_hien_thi.mat_khau_1 }} {{ form_hien_thi.mat_khau_1.errors }}</p>
<p>{{ form_hien_thi.mat_khau_2.label_tag }} {{ form_hien_thi.mat_khau_2 }} {{ form_hien_thi.mat_khau_2.errors }}</p>
{% if form_hien_thi.non_field_errors %} <!-- Lỗi toàn cục của form -->
<ul class="errorlist">
{% for error in form_hien_thi.non_field_errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
-->
<button type="submit">Đăng Ký</button>
</form>
</body>
</html>
Các loại trường (Field) và Tham số phổ biến:
- Loại trường:
CharField,IntegerField,EmailField,DateField,ChoiceField,MultipleChoiceField,FileField, v.v. - Tham số chung:
required=True: Xác định trường có bắt buộc phải nhập hay không.widget=None: Sử dụng widget HTML khác để render trường (ví dụ:forms.PasswordInput,forms.Textarea).label=None: Nhãn hiển thị cho trường.initial=None: Giá trị mặc định ban đầu cho trường.help_text='': Văn bản trợ giúp hiển thị bên cạnh trường.error_messages=None: Dictionary chứa các thông báo lỗi tùy chỉnh cho các trường hợp xác thực cụ thể (ví dụ:{'required': 'Vui lòng không để trống!'}).validators=[]: Danh sách các hàm xác thực tùy chỉnh.disabled=False: NếuTrue, trường sẽ bị vô hiệu hóa trong HTML.
Xác thực tích hợp và Tùy chỉnh:
Django Forms có sẵn các quy tắc xác thực như max_length, min_length, max_value, min_value. Bạn cũng có thể thêm các validator tùy chỉnh:
- Sử dụng Validators tích hợp sẵn:
from django.core.validators import RegexValidator class FormThongTinLienHe(forms.Form): so_dien_thoai = forms.CharField( label='Số điện thoại', validators=[RegexValidator(r'^\+?1?\d{9,15}$', 'Số điện thoại không đúng định dạng.')], help_text='Nhập số điện thoại theo định dạng quốc tế.' ) - Xác thực bằng hàm tùy chỉnh:
from django.core.exceptions import ValidationError def kiem_tra_noi_dung_cam(gia_tri_truong): if 'xau' in gia_tri_truong or 'badword' in gia_tri_truong: raise ValidationError('Nội dung chứa từ ngữ không phù hợp, vui lòng sửa lại.') class FormBaiViet(forms.Form): tieu_de_bai_viet = forms.CharField( label='Tiêu đề', validators=[kiem_tra_noi_dung_cam] ) noi_dung = forms.CharField(widget=forms.Textarea, required=False)
Các phương thức hook clean_ và clean():
Django Forms cung cấp các "hook" để bạn tùy chỉnh quá trình xác thực:
clean_<field_name>(self): Phương thức này được gọi để xác thực một trường cụ thể.- Nó nhận giá trị đã được làm sạch của trường đó.
- Bạn có thể thực hiện kiểm tra cụ thể cho trường này.
- Nếu hợp lệ, trả về giá trị đã làm sạch.
- Nếu không hợp lệ, raise
ValidationError.
from django.contrib.auth import get_user_model # Lấy model User của Django User = get_user_model() class FormTaoTaiKhoan(forms.Form): ten_dang_nhap = forms.CharField(label='Tên đăng nhập') email_nguoi_dung = forms.EmailField(label='Email') mat_khau = forms.CharField(label='Mật khẩu', widget=forms.PasswordInput) def clean_ten_dang_nhap(self): # Xác thực tên đăng nhập: kiểm tra trùng lặp ten = self.cleaned_data['ten_dang_nhap'] if User.objects.filter(username=ten).exists(): raise ValidationError("Tên đăng nhập này đã có người sử dụng.") return ten def clean_email_nguoi_dung(self): # Xác thực email: kiểm tra định dạng và trùng lặp email = self.cleaned_data['email_nguoi_dung'] if not email.endswith('@example.com'): # Ví dụ validation tùy chỉnh raise ValidationError("Email phải thuộc miền @example.com.") if User.objects.filter(email=email).exists(): raise ValidationError("Email này đã được đăng ký tài khoản khác.") return emailclean(self): Phương thức này là một hook toàn cục, được gọi sau khi tất cả các phương thứcclean_<field_name>đã chạy và các lỗi cấp trường đã được xử lý.- Nó nhận toàn bộ
self.cleaned_data, cho phép bạn thực hiện các xác thực liên quan đến nhiều trường. - Nếu hợp lệ, trả về
self.cleaned_data. - Nếu không hợp lệ, raise
ValidationErrorhoặc sử dụngself.add_error('field_name', 'message')để gắn lỗi vào một trường cụ thể.
class FormTaoTaiKhoan(forms.Form): # ... (các trường đã định nghĩa ở trên) mat_khau_nhap_lai = forms.CharField(label='Nhập lại mật khẩu', widget=forms.PasswordInput) def clean(self): # Gọi phương thức clean() của lớp cha để đảm bảo cleaned_data có sẵn du_lieu_da_xac_thuc = super().clean() mk1 = du_lieu_da_xac_thuc.get('mat_khau') mk2 = du_lieu_da_xac_thuc.get('mat_khau_nhap_lai') # Kiểm tra xem hai mật khẩu có khớp nhau không if mk1 and mk2 and mk1 != mk2: self.add_error('mat_khau_nhap_lai', 'Mật khẩu nhập lại không khớp với mật khẩu đã nhập.') # raise ValidationError("Hai mật khẩu không khớp.") # Hoặc raise ValidationError cho lỗi toàn cục # Thêm logic xác thực toàn cục khác nếu cần # ... return du_lieu_da_xac_thuc- Nó nhận toàn bộ