Các thành phần biểu mẫu Django

Các thành phần biểu mẫu Django

Đường dẫn- Các thành phần biểu mẫu Django

  • Giới thiệu thành phần Form
  • Tạo chức năng đăng ký thủ công
  • Sử dụng thành phần Form để đăng ký
  • login2.html
  • Trường và plugin thường dùng
  • Các trường cơ bản
  • Tham số trường
  • Xác thực tích hợp
  • Kiểm tra tùy chỉnh
  • Hàm gắp (hook)
  • Quy trình is_valid
  • Lớp Form: Trường và plugin
  • Giá trị ban đầu
  • Ghi đè thông báo lỗi
  • Mật khẩu (input type='password')
  • Radio đơn
  • Select đơn
  • Select đa
  • Checkbox đơn
  • Checkbox đa
  • Lưu ý với lựa chọn:
  • Cập nhật thời gian thực phương pháp 1
  • Cập nhật thời gian thực phương pháp 2
  • Tất cả trường Django Form
  • Xác thực (biểu thức chính quy)
  • Xác thực tùy chỉnh
  • Áp dụng kiểu Bootstrap
  • Thêm kiểu hàng loạt
  • ModelForm
  • Tham số trong class Meta:
  • Ví dụ sử dụng ModelForm

Giới thiệu thành phần Form

"""
Khi gửi dữ liệu từ biểu mẫu HTML đến backend, chúng ta thường tạo các thẻ nhập liệu và bọc chúng bằng thẻ form.
Tuy nhiên, trong nhiều trường hợp cần kiểm tra đầu vào của người dùng như độ dài, định dạng,...
Thông báo lỗi tương ứng sẽ được hiển thị nếu đầu vào không hợp lệ.
"""
Thành phần Form của Django thực hiện các chức năng trên.
Tóm lại, các chức năng chính của Form bao gồm:

- Tạo thẻ HTML có thể sử dụng
- Kiểm tra dữ liệu người dùng gửi lên
- Giữ lại giá trị nhập trước đó

Tạo chức năng đăng ký thủ công

  • views.py
# Đăng ký
def tai_khoan_moi(request):
    thong_bao_loi = ""
    if request.method == "POST":
        ten_dang_nhap = request.POST.get("ten")
        mk = request.POST.get("mk")
        # Kiểm tra thông tin đăng ký
        if len(ten_dang_nhap) < 6:
            # Độ dài tên dưới 6 ký tự
            thong_bao_loi = "Tên đăng nhập phải có ít nhất 6 ký tự"
        else:
            # Lưu tên và mật khẩu vào DB
            return HttpResponse("Đăng ký thành công")
    return render(request, "dangky.html", {"thong_bao_loi": thong_bao_loi})

  • login.html

<html lang="vi">
<head>
    <meta charset="UTF-8">
    <title>Trang đăng ký</title>
</head>
<body>
<form action="/dk/" method="post">
    {% csrf_token %}
    <p>
        Tên đăng nhập:
        <input type="text" name="ten">
    </p>
    <p>
        Mật khẩu:
        <input type="password" name="mk">
    </p>
    <p>
        <input type="submit" value="Đăng ký">
        <p style="color: red">{{ thong_bao_loi }}</p>
    </p>
</form>
</body>
</html>

Sử dụng thành phần Form để đăng ký

#Chức năng thành phần Form
1. Tạo thẻ input
2. Kiểm tra dữ liệu gửi lên
3. Hiển thị thông báo lỗi

  • views.py
  • Định nghĩa lớp DangKyForm:
#Cấu trúc cơ bản
from django import forms #Nhập module thành phần

#Tạo lớp theo yêu cầu của Django Form
class DangKyForm(forms.Form):
    ten = forms.CharField(label="Tên đăng nhập")
    mk = forms.CharField(label="Mật khẩu")

  • Viết hàm xem:
# Sử dụng thành phần Form đăng ký
def dang_ky_moi(request):
    form = DangKyForm()
    if request.method == "POST":
        # Khởi tạo form với dữ liệu POST
        form = DangKyForm(request.POST)
        # Gọi phương thức kiểm tra dữ liệu
        if form.is_valid():
            return HttpResponse("Đăng ký thành công")
    return render(request, "dangky2.html", {"form": form})

  • login2.html


<html lang="vi">
<head>
    <meta charset="UTF-8">
    <title>Đăng ký 2</title>
</head>
<body>
    <form action="/dk2/" method="post" novalidate autocomplete="off">
        {% csrf_token %}
        <div>
            <label for="{{ form.ten.id_for_label }}">{{ form.ten.label }}</label>
            {{ form.ten }} {{ form.ten.errors.0 }}
        </div>
        <div>
            <label for="{{ form.mk.id_for_label }}">{{ form.mk.label }}</label>
            {{ form.mk }} {{ form.mk.errors.0 }}
        </div>
        <div>
            <input type="submit" class="btn btn-success" value="Đăng ký">
        </div>
    </form>
</body>
</html>

Hiệu ứng trang web xác minh chức năng Form:
•	Giao diện được tạo bởi đối tượng Form	-->Tạo thẻ HTML
•	Khi tên/mật khẩu rỗng hoặc sai → hiển thị lỗi	-->Kiểm tra đầu vào
•	Sau khi nhập sai → giữ giá trị trước đó	-->Giữ lại dữ liệu

Trường và plugin thường dùng

Các trường cơ bản

CharField    #Chuỗi
ChoiceField  #Lựa chọn
MultipleChoiceField#Lựa chọn đa

Tham số trường

required=True,               Cho phép rỗng
widget=None,                 Plugin HTML
label=None,                  Tạo nhãn
initial=None,                Giá trị mặc định
error_messages=None,         {'required': 'Không được rỗng', 'invalid': 'Định dạng sai'}
validators=[],               Quy tắc kiểm tra
disabled=False,              Cho phép chỉnh sửa

Xác thực tích hợp

required=True   #Bắt buộc điền
min_length      #Độ dài tối thiểu
max_length	    #Độ dài tối đa

Kiểm tra tùy chỉnh

#Khi xác thực tích hợp không đủ
#Tạo hàm
def kiem_tra_ten(gia_tri):
    # Quy tắc hợp lệ → không xử lý
    # Không hợp lệ → ném ngoại lệ
    if 'từ cấm' in gia_tri:
        raise ValidationError('Vi phạm nội quy')

Hàm gắp (hook)

  • Gắp cục bộ (kiểm tra trường cụ thể)
def clean_truong(self):
    # Gắp cục bộ
    # Hợp lệ → trả về giá trị
    # Không hợp lệ → ném ngoại lệ
    gia_tri = self.cleaned_data.get('trường')
    if 'từ cấm' in gia_tri:
    	raise ValidationError('Nội dung không phù hợp')
    else:
        return gia_tri

  • Gắp toàn cục
def clean(self):
    # Gắp toàn cục
    # Hợp lệ → trả về dữ liệu
    # Không hợp lệ → ném ngoại lệ '__all__'
    mk = self.cleaned_data.get('mk')
    xac_nhan = self.cleaned_data.get('xac_nhan')
    if mk == xac_nhan:
        return self.cleaned_data
    else:
        #self.add_error("xac_nhan","Mật khẩu không khớp")  #Tùy chỉnh thông báo
        raise ValidationError('Mật khẩu không khớp')

Quy trình is_valid

- 1 Thực hiện full_clean():
  - Tạo từ điển lỗi
  - Tạo từ điển dữ liệu đã làm sạch

- 2 Thực hiện _clean_fields:
  - Lặp tất cả các trường
  - Lấy giá trị kiểm tra (xác thực tích hợp, kiểm tra tùy chỉnh)
- Hợp lệ
  - self.cleaned_data[name] = value 
  - Nếu có gắp cục bộ → kiểm tra:
    - Hợp lệ → self.cleaned_data[name] = value 
    - Không hợp lệ → self._errors thêm lỗi, xóa giá trị khỏi cleaned_data

- Không hợp lệ
  - self._errors thêm lỗi

- 3 Thực hiện gắp toàn cục clean()


Tóm tắt quy trình:
#(1) is_valid() kiểm tra self.errors, có giá trị → false
#(2) Kiểm tra self._errors, rỗng → self.full_clean(), ngược lại → self._errors
#(3) full_clean() tạo self._errors và cleaned_data
#(4) self._clean_fields() → kiểm tra trường
#(5) field.clean(value) thực hiện kiểm tra
#(6) Thêm kết quả vào self._errors và cleaned_data
#(7) Gắp clean_XX nếu tồn tại → thêm lỗi vào self._errors
#(8) Hoàn thành quy trình

Lớp Form: Trường và plugin

  • Tạo lớp Form cần sử dụng trường và plugin. Trường kiểm tra dữ liệu, plugin tạo HTML
  • Giá trị ban đầu

#Giá trị mặc định trong input
class DangNhapForm(forms.Form):
    ten_dang_nhap = forms.CharField(
        min_length=8,
        label="Tên",
        initial="NguyenVanA"  # Thiết lập mặc định
    )
    mk = forms.CharField(min_length=6, label="Mật khẩu")

  • Ghi đè thông báo lỗi

class DangNhapForm(forms.Form):
    ten_dang_nhap = forms.CharField(
        min_length=8,
        label="Tên",
        initial="NguyenVanA",
        error_messages={
            "required": "Không được để trống",
            "invalid": "Định dạng sai",
            "min_length": "Tên tối thiểu 8 ký tự"
        }
    )
    mk = forms.CharField(min_length=6, label="Mật khẩu")

  • Mật khẩu (input type='password')

class DangNhapForm(forms.Form):
    ...
    mk = forms.CharField(
        min_length=6,
        label="Mật khẩu",
        widget=forms.widgets.PasswordInput(attrs={'class': 'mk1'}, render_value=True)
    )

  • Radio đơn

class DangNhapForm(forms.Form):
    ten_dang_nhap = forms.CharField(
        min_length=8,
        label="Tên",
        initial="NguyenVanA",
        error_messages={
            "required": "Không được để trống",
            "invalid": "Định dạng sai",
            "min_length": "Tên tối thiểu 8 ký tự"
        }
    )
    mk = forms.CharField(min_length=6, label="Mật khẩu")
    gioi_tinh = forms.fields.ChoiceField(
        choices=((1, "Nam"), (2, "Nữ"), (3, "Bí mật")),
        label="Giới tính",
        initial=3,
        widget=forms.widgets.RadioSelect()
    )

  • Select đơn

class DangNhapForm(forms.Form):
    ...
    thich = forms.fields.ChoiceField(
        choices=((1, "Bóng rổ"), (2, "Bóng đá"), (3, "Xổ số"), ),
        label="Sở thích",
        initial=3,
        widget=forms.widgets.Select()
    )

  • Select đa

class DangNhapForm(forms.Form):
    ...
    thich = forms.fields.MultipleChoiceField(
        choices=((1, "Bóng rổ"), (2, "Bóng đá"), (3, "Xổ số"), ),
        label="Sở thích",
        initial=[1, 3],
        widget=forms.widgets.SelectMultiple()
    )

  • Checkbox đơn

class DangNhapForm(forms.Form):
    ...
    luu = forms.fields.ChoiceField(
        label="Ghi nhớ mật khẩu",
        initial="checked",
        widget=forms.widgets.CheckboxInput()
    )

  • Checkbox đa

class DangNhapForm(forms.Form):
    ...
    thich = forms.fields.MultipleChoiceField(
        choices=((1, "Bóng rổ"), (2, "Bóng đá"), (3, "Xổ số"),),
        label="Sở thích",
        initial=[1, 3],
        widget=forms.widgets.CheckboxSelectMultiple()
    )

Lưu ý về lựa chọn:

Khi sử dụng thẻ lựa chọn, lưu ý rằng options có thể lấy từ DB nhưng là trường tĩnh nên không cập nhật theo thời gian thực.
Phải tùy chỉnh __init__ để đạt được điều này.

  • Cập nhật thời gian thực phương pháp 1

from django.forms import Form
from django.forms import widgets
from django.forms import fields

 
class FormMoi(Form):
 
    nguoi_dung = fields.ChoiceField(
        # choices=((1, 'Hà Nội'), (2, 'TP.HCM'),),
        initial=2,
        widget=widgets.Select
    )
 
    def __init__(self, *args, **kwargs):
        super(FormMoi,self).__init__(*args, **kwargs)
        # self.fields['nguoi_dung'].choices = ((1, 'Hà Nội'), (2, 'TP.HCM'),)
        # Hoặc
        self.fields['nguoi_dung'].choices = models.LopHoc.objects.all().values_list('id','ten')

  • Cập nhật thời gian thực phương pháp 2

from django import forms
from django.forms import fields
from django.forms import models as form_model

 
class FormThongTin(forms.Form):
    tac_gia = form_model.ModelMultipleChoiceField(queryset=models.LoaiTin.objects.all())  # Đa chọn
    # tac_gia = form_model.ModelChoiceField(queryset=models.LoaiTin.objects.all())  # Đơn chọn

Tất cả trường Django Form

Field
    required=True,               Cho phép rỗng
    widget=None,                 Plugin HTML
    label=None,                  Tạo nhãn (mặc định tên trường viết hoa chữ cái đầu)
    initial=None,                Giá trị mặc định
    help_text='',                Hướng dẫn (hiển thị bên cạnh nhãn)
    error_messages=None,         {'required': 'Không được rỗng', 'invalid': 'Định dạng sai'}
    validators=[],               Quy tắc kiểm tra
    localize=False,              Hỗ trợ địa phương hóa
    disabled=False,              Cho phép chỉnh sửa
    label_suffix=None            Ký hiệu sau nhãn
 
 
CharField(Field)
    max_length=None,             Độ dài tối đa
    min_length=None,             Độ dài tối thiểu
    strip=True                   Xóa khoảng trắng đầu/cuối
 
IntegerField(Field)
    max_value=None,              Giá trị lớn nhất
    min_value=None,              Giá trị nhỏ nhất
 
FloatField(IntegerField)
    ...
 
DecimalField(IntegerField)
    max_value=None,              Giá trị lớn nhất
    min_value=None,              Giá trị nhỏ nhất
    max_digits=None,             Tổng số chữ số
    decimal_places=None,         Số chữ số thập phân
 
BaseTemporalField(Field)
    input_formats=None          Định dạng thời gian   
 
DateField(BaseTemporalField)    Định dạng: 2015-09-01
TimeField(BaseTemporalField)    Định dạng: 11:12
DateTimeField(BaseTemporalField)Định dạng: 2015-09-01 11:12
 
DurationField(Field)            Khoảng thời gian: %d %H:%M:%S.%f
    ...
 
RegexField(CharField)
    regex,                      Biểu thức chính quy
    max_length=None,            Độ dài tối đa
    min_length=None,            Độ dài tối thiểu
    error_message=None,         Bỏ qua, dùng error_messages={'invalid': '...'}
 
EmailField(CharField)      
    ...
 
FileField(Field)
    allow_empty_file=False     Cho phép file rỗng
 
ImageField(FileField)      
    ...
    Cần PIL/Pillow
    Lưu ý:
        - form có enctype="multipart/form-data"
        - view có obj = FormMoi(request.POST, request.FILES)
 
URLField(Field)
    ...
 
 
BooleanField(Field)  
    ...
 
NullBooleanField(BooleanField)
    ...
 
ChoiceField(Field)
    ...
    choices=(),                Lựa chọn, ví dụ: choices = ((0,'Hà Nội'),(1,'TP.HCM'),)
    required=True,             Bắt buộc
    widget=None,               Plugin, mặc định select
    label=None,                Nội dung nhãn
    initial=None,              Giá trị mặc định
    help_text='',              Hướng dẫn
 
 
ModelChoiceField(ChoiceField)
    ...                        django.forms.models.ModelChoiceField
    queryset,                  Truy vấn dữ liệu DB
    empty_label="---------",   Nội dung rỗng
    to_field_name=None,        Trường đại diện cho value
    limit_choices_to=None      Lọc queryset lần hai
     
ModelMultipleChoiceField(ModelChoiceField)
    ...                        django.forms.models.ModelMultipleChoiceField
 
 
     
TypedChoiceField(ChoiceField)
    coerce = lambda val: val   Chuyển đổi giá trị
    empty_value= ''            Giá trị rỗng mặc định
 
MultipleChoiceField(ChoiceField)
    ...
 
TypedMultipleChoiceField(MultipleChoiceField)
    coerce = lambda val: val   Chuyển đổi từng giá trị
    empty_value= ''            Giá trị rỗng mặc định
 
ComboField(Field)
    fields=()                  Nhiều quy tắc kiểm tra, ví dụ: độ dài 20 và định dạng email
                               fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),])
 
MultiValueField(Field)
    PS: Lớp trừu tượng, có thể kết hợp nhiều trường, cần MultiWidget
 
SplitDateTimeField(MultiValueField)
    input_date_formats=None,   Định dạng ngày: ['%Y--%m--%d', '%m%d/%Y', '%m/%d/%y']
    input_time_formats=None    Định dạng giờ: ['%H:%M:%S', '%H:%M:%S.%f', '%H:%M']
 
FilePathField(ChoiceField)     Hiển thị file trong thư mục
    path,                      Đường dẫn
    match=None,                Biểu thức chính quy
    recursive=False,           Đệ quy
    allow_files=True,          Cho phép file
    allow_folders=False,       Cho phép folder
    required=True,
    widget=None,
    label=None,
    initial=None,
    help_text=''
 
GenericIPAddressField
    protocol='both',           Hỗ trợ IP
    unpack_ipv4=False          Giải mã IPv4, cần protocol=both
 
SlugField(CharField)           Số, chữ, gạch dưới, gạch nối
    ...
 
UUIDField(CharField)           Định dạng UUID

Xác thực (biểu thức chính quy)

from django.forms import Form
from django.forms import widgets
from django.forms import fields
from django.core.validators import RegexValidator
 
class FormMoi(Form):
    nguoi_dung = fields.CharField(
        validators=[RegexValidator(r'^[0-9]+$', 'Vui lòng nhập số'), RegexValidator(r'^159[0-9]+$', 'Số phải bắt đầu bằng 159')],
    )

  • Xác thực tùy chỉnh

import re
from django.forms import Form
from django.forms import widgets
from django.forms import fields
from django.core.exceptions import ValidationError
 
 
# Quy tắc kiểm tra tùy chỉnh
def kiem_tra_so_dien_thoai(gia_tri):
    regex = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$')
    if not regex.match(gia_tri):
        raise ValidationError('Số điện thoại không hợp lệ')
 
 
class FormDangKy(Form):
 
 
    tieu_de = fields.CharField(max_length=20,
                            min_length=5,
                            error_messages={'required': 'Không được để trống tiêu đề',
                                            'min_length': 'Tối thiểu 5 ký tự',
                                            'max_length': 'Tối đa 20 ký tự'},
                            widget=widgets.TextInput(attrs={'class': "form-control",
                                                          'placeholder': 'Từ 5-20 ký tự'}))
 
 
    # Sử dụng quy tắc kiểm tra tùy chỉnh
    so_dt = fields.CharField(validators=[kiem_tra_so_dien_thoai, ],
                            error_messages={'required': 'Không được để trống số điện thoại'},
                            widget=widgets.TextInput(attrs={'class': "form-control",
                                                          'placeholder': 'Số điện thoại'}))
 
    email = fields.EmailField(required=False,
                            error_messages={'required': 'Không được để trống email','invalid': 'Định dạng email sai'},
                            widget=widgets.TextInput(attrs={'class': "form-control", 'placeholder': 'Email'}))

Áp dụng kiểu Bootstrap


<html lang="vi">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="x-ua-compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css">
  <title>Đăng nhập</title>
</head>
<body>
<div class="container">
  <div class="row">
    <form action="/login2/" method="post" novalidate class="form-horizontal">
      {% csrf_token %}
      <div class="form-group">
        <label for="{{ form.ten_dang_nhap.id_for_label }}"
               class="col-md-2 control-label">{{ form.ten_dang_nhap.label }}</label>
        <div class="col-md-10">
          {{ form.ten_dang_nhap }}
          <span class="help-block">{{ form.ten_dang_nhap.errors.0 }}</span>
        </div>
      </div>
      <div class="form-group">
        <label for="{{ form.mk.id_for_label }}" class="col-md-2 control-label">{{ form.mk.label }}</label>
        <div class="col-md-10">
          {{ form.mk }}
          <span class="help-block">{{ form.mk.errors.0 }}</span>
        </div>
      </div>
      <div class="form-group">
      <label class="col-md-2 control-label">{{ form.gioi_tinh.label }}</label>
        <div class="col-md-10">
          <div class="radio">
            {% for radio in form.gioi_tinh %}
              <label for="{{ radio.id_for_label }}">
                {{ radio.tag }}{{ radio.choice_label }}
              </label>
            {% endfor %}
          </div>
        </div>
      </div>
      <div class="form-group">
        <div class="col-md-offset-2 col-md-10">
          <button type="submit" class="btn btn-default">Đăng ký</button>
        </div>
      </div>
    </form>
  </div>
</div>

<script src="/static/jquery-3.2.1.min.js"></script>
<script src="/static/bootstrap/js/bootstrap.min.js"></script>
</body>
</html>

Thêm kiểu hàng loạt

  • Ghi đè init của lớp form
class DangNhapForm(forms.Form):
    ten_dang_nhap = forms.CharField(
        min_length=8,
        label="Tên",
        initial="NguyenVanA",
        error_messages={
            "required": "Không được để trống",
            "invalid": "Định dạng sai",
            "min_length": "Tên tối thiểu 8 ký tự"
        }
    ...

    def __init__(self, *args, **kwargs):
        super(DangNhapForm, self).__init__(*args, **kwargs)
        for truong in iter(self.fields):
            self.fields[truong].widget.attrs.update({
                'class': 'form-control'
            })

ModelForm

  • Kết hợp form và model
class FormSach(forms.ModelForm):

    class Meta:
        model = models.Sach
        fields = "__all__"
        labels = {
            "ten": "Tên sách",
            "gia": "Giá"
        }
        widgets = {
            "mat_khau": forms.widgets.PasswordInput(attrs={"class": "mk1"}),
        }

  • Tham số trong class Meta:

model = models.HocSinh  # Lớp Model tương ứng
fields = "__all__"  # Tất cả trường
exclude = None  # Loại trừ
labels = None  # Nhãn
help_texts = None  # Hướng dẫn
widgets = None  # Plugin
error_messages = None  # Thông báo lỗi

Ví dụ sử dụng ModelForm

  • Tạo lớp FormChinhSuaBlog
from blog import models
from django import forms

class FormChinhSuaBlog(forms.ModelForm):
    class Meta:
        model = models.Blog  # Lớp tạo form
        fields = '__all__'   # Tất cả trường
        exclude = ['ma_so',]	 # Loại trừ
        error_messages = {
            'ten': {'required': 'Không được để trống'}, # Thông báo lỗi
            'tac_gia': {'required': 'Không được để trống'}, 
        }

    def __init__(self, *args, **kwargs):     # Ghi đè __init__ để thêm class cho input
        super().__init__(*args, **kwargs)
        from multiselectfield.forms.fields import MultiSelectFormField
        for ten_truong, truong in self.fields.items():
            if not isinstance(truong, MultiSelectFormField):
                truong.widget.attrs.update({'class': 'form-control'})

  • views
from blog import models
from blog.forms import FormChinhSuaBlog
from django.shortcuts import render, redirect

def sua_blog(request, ma_so=None):
    doi_tuong = models.Blog.objects.filter(id=ma_so).first() # Lấy đối tượng
    tieu_de = 'Tạo mới blog' if not ma_so else 'Chỉnh sửa blog'     # Tựa đề
    form = FormChinhSuaBlog(instance=doi_tuong)          # Truyền đối tượng
    if request.method == 'POST':                   # Yêu cầu POST
        form = FormChinhSuaBlog(data=request.POST, instance=doi_tuong) # Kiểm tra dữ liệu
        if form.is_valid(): # Kiểm tra hợp lệ
            form.save()     # Lưu
            return redirect('blog:danh_sach_blog') # Chuyển hướng
    return render(request, 'sua_blog.html', {'form': form, 'tieu_de': tieu_de})

  • html
{% extends 'base.html' %}

{% block title %}
<h1>
    {{ tieu_de }}    <!--Tựa đề-->
</h1>
{% endblock %}

{% block content %}
<div class="panel-body">
    <form class="form-horizontal" action="" method="post" novalidate>
        {% csrf_token %}  <!--Kiểm tra csrf-->
        {% for truong in form %}
        <div class="form-group {% if truong.errors  %} has-error {% endif %}"><!--Hiển thị lỗi màu đỏ-->
            <label for="{{ truong.id_for_label }}" {% if not truong.field.required %} style="color: dimgrey" {% endif %}
                   class="col-sm-2 control-label">{{ truong.label }}</label>
            <div class="col-sm-5">
                {{ truong }}
                <span class="help-block">{{ truong.errors.0}}</span><!--Thông báo lỗi-->
            </div>
        </div>
        {% endfor %}
        <div class="form-group">
            <div class="col-sm-offset-2 col-sm-10">
                <button type="submit" class="btn btn-default">Gửi</button>
            </div>
        </div>
    </form>
</div>
{% endblock %}

Thẻ: Django form-components Bootstrap ModelForm validation

Đăng vào ngày 1 tháng 7 lúc 01:33