Django Form和ModelForm组件

一 Form主要功能

 总结一下,其实form组件的主要功能如下:

    生成页面可用的HTML标签

    对用户提交的数据进行校验

    保留上次输入内容

 

二 Form常用字段与插件

创建Form类时,主要涉及到 【字段】 和 【插件】,字段用于对用户请求数据的验证,插件用于自动生成HTML;

input - text

from django import forms

class LoginForm(forms.Form):

    username = forms.CharField(
        max_length=16,
        min_length=6,
        label='用户名',
        # widget=forms.widgets.TextInput(attrs={'class':'form-control'})
    )

input-password

    password = forms.CharField(
        label='密码',
        # widget=forms.widgets.PasswordInput,#生成密码框
        widget=forms.widgets.PasswordInput(attrs={'class':'form-control'}),  # 也可以加括号通过attrs给它设置它的标签属性

    )

单选按钮

initial 设置默认值

    sex = forms.ChoiceField(
        label='性别',
        initial=3, #设置默认值
        choices=((1, ""), (2, ""), (3, "保密")),
        widget=forms.widgets.RadioSelect(),
    )

单选下拉框

    city = forms.ChoiceField(
        label='城市',
        initial=3,  # 设置默认值
        choices=((1, "北京"), (2, "上海"), (3, "西安")),
        widget=forms.widgets.Select(),
    )

多选下拉框

    girls = forms.MultipleChoiceField(
        label='爱好',
        initial=[1, 3],  # 设置默认值
        choices=((1, "王昭君"), (2, "西施"), (3, "杨贵妃")),
        widget=forms.widgets.SelectMultiple(),
    )

多选checkbox按钮

    hobby = forms.MultipleChoiceField(
        label='爱好',
        initial=[1,3],  # 设置默认值
        choices=((1, "篮球"), (2, "足球"), (3, "羽毛球")),
        widget=forms.widgets.CheckboxSelectMultiple(),
    )

单选checkbox

    status = forms.ChoiceField(
        label='同意用户协议',
        choices=(('True', 1), ('False', 2)),
        widget=forms.widgets.CheckboxInput(),
    )

date类型 input框

    birthday = forms.CharField(
        label='生日',
        widget=forms.widgets.TextInput(attrs={'type':'date'})
    )

模板中使用

    <div>
        <label for="">{{ form_obj.username.label }}</label>
        {{ form_obj.username }}
    </div>

 

三 Form所有内置字段

  内置字段

Field
    required=True,               是否允许为空
    widget=None,                 HTML插件
    label=None,                  用于生成Label标签或显示内容
    initial=None,                初始值
    help_text='',                帮助信息(在标签旁边显示)
    error_messages=None,         错误信息 {'required': '不能为空', 'invalid': '格式错误'}
    validators=[],               自定义验证规则
    localize=False,              是否支持本地化
    disabled=False,              是否可以编辑
    label_suffix=None            Label内容后缀
 
 
CharField(Field)
    max_length=None,             最大长度
    min_length=None,             最小长度
    strip=True                   是否移除用户输入空白
 
IntegerField(Field)
    max_value=None,              最大值
    min_value=None,              最小值
 
FloatField(IntegerField)
    ...
 
DecimalField(IntegerField)
    max_value=None,              最大值
    min_value=None,              最小值
    max_digits=None,             总长度
    decimal_places=None,         小数位长度
 
BaseTemporalField(Field)
    input_formats=None          时间格式化   
 
DateField(BaseTemporalField)    格式:2015-09-01
TimeField(BaseTemporalField)    格式:11:12
DateTimeField(BaseTemporalField)格式:2015-09-01 11:12
 
DurationField(Field)            时间间隔:%d %H:%M:%S.%f
    ...
 
RegexField(CharField)
    regex,                      自定制正则表达式
    max_length=None,            最大长度
    min_length=None,            最小长度
    error_message=None,         忽略,错误信息使用 error_messages={'invalid': '...'}
 
EmailField(CharField)      
    ...
 
FileField(Field)
    allow_empty_file=False     是否允许空文件
 
ImageField(FileField)      
    ...
    注:需要PIL模块,pip3 install Pillow
    以上两个字典使用时,需要注意两点:
        - form表单中 enctype="multipart/form-data"
        - view函数中 obj = MyForm(request.POST, request.FILES)
 
URLField(Field)
    ...
 
 
BooleanField(Field)  
    ...
 
NullBooleanField(BooleanField)
    ...
 
ChoiceField(Field)
    ...
    choices=(),                选项,如:choices = ((0,'上海'),(1,'北京'),)
    required=True,             是否必填
    widget=None,               插件,默认select插件
    label=None,                Label内容
    initial=None,              初始值
    help_text='',              帮助提示
 
 
ModelChoiceField(ChoiceField)
    ...                        django.forms.models.ModelChoiceField
    queryset,                  # 查询数据库中的数据
    empty_label="---------",   # 默认空显示内容
    to_field_name=None,        # HTML中value的值对应的字段
    limit_choices_to=None      # ModelForm中对queryset二次筛选
     
ModelMultipleChoiceField(ModelChoiceField)
    ...                        django.forms.models.ModelMultipleChoiceField
 
 
     
TypedChoiceField(ChoiceField)
    coerce = lambda val: val   对选中的值进行一次转换
    empty_value= ''            空值的默认值
 
MultipleChoiceField(ChoiceField)
    ...
 
TypedMultipleChoiceField(MultipleChoiceField)
    coerce = lambda val: val   对选中的每一个值进行一次转换
    empty_value= ''            空值的默认值
 
ComboField(Field)
    fields=()                  使用多个验证,如下:即验证最大长度20,又验证邮箱格式
                               fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),])
 
MultiValueField(Field)
    PS: 抽象类,子类中可以实现聚合多个字典去匹配一个值,要配合MultiWidget使用
 
SplitDateTimeField(MultiValueField)
    input_date_formats=None,   格式列表:['%Y--%m--%d', '%m%d/%Y', '%m/%d/%y']
    input_time_formats=None    格式列表:['%H:%M:%S', '%H:%M:%S.%f', '%H:%M']
 
FilePathField(ChoiceField)     文件选项,目录下文件显示在页面中
    path,                      文件夹路径
    match=None,                正则匹配
    recursive=False,           递归下面的文件夹
    allow_files=True,          允许文件
    allow_folders=False,       允许文件夹
    required=True,
    widget=None,
    label=None,
    initial=None,
    help_text=''
 
GenericIPAddressField
    protocol='both',           both,ipv4,ipv6支持的IP格式
    unpack_ipv4=False          解析ipv4地址,如果是::ffff:192.0.2.1时候,可解析为192.0.2.1, PS:protocol必须为both才能启用
 
SlugField(CharField)           数字,字母,下划线,减号(连字符)
    ...
 
UUIDField(CharField)           uuid类型

 

四 字段校验

RegexValidator验证器

from django.core.validators import RegexValidator
 
class MyForm(Form):
    user = fields.CharField(
        validators=[RegexValidator(r'^[0-9]+$', '请输入数字'), RegexValidator(r'^159[0-9]+$', '数字必须以159开头')],
    )

自定义验证函数

from django.core.exceptions import ValidationError
 
 
# 自定义验证规则
def mobile_validate(value):
    mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$')
    if not mobile_re.match(value):
        raise ValidationError('手机号码格式错误')  #自定义验证规则的时候,如果不符合你的规则,需要自己发起错误
 
 
class PublishForm(Form):
 
 
    title = fields.CharField(max_length=20,
                            min_length=5,
                            error_messages={'required': '标题不能为空',
                                            'min_length': '标题最少为5个字符',
                                            'max_length': '标题最多为20个字符'},
                            widget=widgets.TextInput(attrs={'class': "form-control",
                                                          'placeholder': '标题5-20个字符'}))
 
 
    # 使用自定义验证规则
    phone = fields.CharField(validators=[mobile_validate, ],
                            error_messages={'required': '手机不能为空'},
                            widget=widgets.TextInput(attrs={'class': "form-control",
                                                          'placeholder': u'手机号码'}))
 
    email = fields.EmailField(required=False,
                            error_messages={'required': u'邮箱不能为空','invalid': u'邮箱格式错误'},
                            widget=widgets.TextInput(attrs={'class': "form-control", 'placeholder': u'邮箱'}))

五钩子方法

局部钩子 

针对于某一个字段的校验,校验顺序:首先走定义的校验,定义的校验走过后,在走局部钩子,局部钩子通过后返回通过校验字段的值。
写法:clean_字段名称
password先走基本的校验,校验走完了后,在走clean_password函数校验。如果校验不过返回html页面错误文案
class LoginForm(forms.Form):

    #input - text
    username = forms.CharField(
        required=True, #判断是否可以为空,默认是不能为空,默认是True
        max_length=16,
        min_length=6,
        label='用户名',
        # #方式1
        # validators=[RegexValidator(r'^13[0-9]{9}$','手机号格式错误'),],
        #方法2
        validators = [moblie_Validator,],
        strip=True,#移除用户输入内容的前后空格
        # widget=forms.widgets.TextInput(attrs={'class':'form-control'})
        error_messages={
            "required": "用户名不能为空",
            # "invalid": "格式错误",
            "min_length": "用户名最短8位"
        }
    )

    # input - password
    password = forms.CharField(
        required=True,
        label='密码',
        min_length=6,
        max_length=10,
        widget=forms.widgets.PasswordInput,#生成密码框
        # widget=forms.widgets.PasswordInput(attrs={'class':'form-control'}),  # 也可以加括号通过attrs给它设置它的标签属性
        error_messages={
            "required": "密码不能为空",
            "invalid": "格式错误",
            "min_length": "用户名最短6位最长10位"
        }
    )
  
    #局部钩子 clean_字段名称
        #局部钩子:针对于某一个字段的校验,校验顺序:首先走定义的校验,定义的校验走过后,在走局部钩子,局部钩子通过后返回通过校验字段的值
    def clean_password(self):
        value = self.cleaned_data.get('password')
        if '1111111' in value:
            raise ValidationError('含有敏感词汇:1111111')
        else:
            return value
#方式1:一般方式
def register(request):
    form_obj = LoginForm() #创建对象只是为了生成一些标签
    if request.method == 'GET':
        return render(request, 'register.html',{'form_obj':form_obj})

    else:
        form_obj = LoginForm(request.POST) #现在创建对象是为了验证数据的正确性
        print(form_obj.is_valid())
        if form_obj.is_valid():
            print(form_obj.cleaned_data)
            return HttpResponse('ok')
        else:
            print(form_obj.errors)
            return render(request,'register.html',{'form_obj':form_obj})

全局钩子

针对与全局:两次输入密码完成后,提交走校验。只抛一个错误

写法:必须使用clean函数

    #全局钩子
    def clean(self):
        value = self.cleaned_data
        p1 = value.get('password')
        p2 = value.get('r_password')
        if p1==p2:
            return value
        else:
            # raise ValidationError('两次密码输入不一致')
            self.add_error('r_password','两次输入密码不一致')
            raise ValidationError('两次密码输入不一致')
    

html页面使用

<form action="{% url 'register' %}" method="post" novalidate>
    {% csrf_token %}
    {#    用户名:<input type="text" name="username">#}
    {#    密码:<input type="text" name="password">#}
    <div>
        <label for="">{{ form_obj.username.label }}</label>
        {{ form_obj.username }}
        {#        {{ form_obj.errors }}#}
        <span style="color: red;font-size: 14px">{{ form_obj.username.errors.0 }}</span>
    </div>

    <div>
        <label for="">{{ form_obj.password.label }}</label>
        {{ form_obj.password }}
        {#        {{ form_obj.errors }}#}
        <span style="color: red;font-size: 14px">{{ form_obj.password.errors.0 }}</span>
    </div>

    <div>
        <label for="">{{ form_obj.r_password.label }}</label>
        {{ form_obj.r_password }}
        {#        {{ form_obj.errors }}#}
        <span style="color: red;font-size: 14px">{{ form_obj.r_password.errors.0 }}</span>
    </div>
    <input type="submit">
</form>

 

 ModelForm:简单易用

  通常在Django项目中,我们编写的大部分都是与Django 的模型紧密映射的表单。 举个例子,你也许会有个Book 模型,并且你还想创建一个form表单用来添加和编辑书籍信息到这个模型中。 在这种情况下,在form表单中定义字段将是冗余的,因为我们已经在模型中定义了那些字段。

  基于这个原因,Django 提供一个辅助类来让我们可以从Django 的模型创建Form,这就是ModelForm。

  modelForm定义

    form与model的终极结合,会根据你model中的字段转换成对应的form字段,并且并你生成标签等操作。

    比如你的models中的表是下面的内容:

class Book(models.Model):

    nid = models.AutoField(primary_key=True)
    title = models.CharField(max_length=32)
    good = models.IntegerField(default=1)
    comment = models.IntegerField(default=1)
    publishDate=models.DateField()
    price=models.DecimalField(max_digits=5,decimal_places=2)#Decimal ,max_digits整数加小数位数,decimal_places小数位数

    # 与Publish建立一对多的关系,外键字段建立在多的一方,字段publish如果是外键字段,那么它自动是int类型
    publishs=models.ForeignKey(to="Publish",to_field="id",on_delete=models.CASCADE)
    #foreignkey里面可以加很多的参数,都是需要咱们学习的,慢慢来,to指向表,to_field指向你关联的字段,不写这个,默认会自动关联主键字段,on_delete级联删除字段名称不需要写成publish_id,orm在翻译foreignkey的时候会自动给你这个字段拼上一个_id,这个字段名称在数据库里面就自动变成了publish_id
    # 与Author表建立多对多的关系,ManyToManyField可以建在两个模型中的任意一个,自动创建第三张表,并且注意一点,你查看book表的时候,你看不到这个字段,因为这个字段就是创建第三张表的意思,不是创建字段的意思,所以只能说这个book类里面有authors这个字段属性
    authors=models.ManyToManyField(to='Author',)

modelform类的写法。

from django.core.exceptions import ValidationError
#ModelForm形式
class BookModelForm(forms.ModelForm):
    #可以重写modle表定义的规则
    # title = forms.CharField(max_length=15,min_length=6)

    class Meta:
        model = models.Book
        #加载所有字段
        # fields = '__all__'
        #单独指定需要加载的字段:只加载title字段
        # fields = ['title']
        #排除那些字段:排除title字段加载book表中其他所有字段
        exclude = ['comment','good']

        labels = {
            'title':'书籍名称',
            'publishDate': '出版日期',
            'price': '价格',
            'publishs':'出版社',
            'authors': '作者',
        }
        #给字段设置样式
        widgets = {
            'publishDate': forms.widgets.TextInput(attrs={'type':'date'})
        }

        error_messages={
            'title':{'required':'不能为空'},
            'price': {'required': '不能为空'},
            'publishDate': {'required': '不能为空'},
            'publishs': {'required': '不能为空'},
            'authors': {'required': '不能为空'},
        }

    #定义局部钩子
    def clean_title(self):
        value = self.cleaned_data.get('title')
        if '666' in value:
            raise ValidationError('输入敏感字符')
        else:
            return value

    #全局钩子
    def clean(self):
        pass
    def __init__(self,*args,**kwargs):
        super().__init__(*args,**kwargs)
        for field in self.fields.values():
            field.widget.attrs.update({'class':'form-control'})

class Meta下常用参数:

model = models.Book  # 对应的Model中的类
fields = "__all__"  # 字段,如果是__all__,就是表示列出所有的字段
exclude = None  # 排除的字段
labels = None  # 提示信息
help_texts = None  # 帮助提示信息
widgets = None  # 自定义插件
error_messages = None  # 自定义错误信息
error_messages = {
    'title':{'required':'不能为空',...} #每个字段的所有的错误都可以写,...是省略的意思,复制黏贴我代码的时候别忘了删了...
}

批量添加样式:和form的一样

 

    #批量添加样式
    def __init__(self,*args,**kwargs):
        super().__init__(*args,**kwargs)
        for field in self.fields.values():
            field.widget.attrs.update({'class':'form-control'})

 

  ModelForm的验证

    与普通的Form表单验证类型类似,ModelForm表单的验证在调用is_valid() 或访问errors 属性时隐式调用。

    我们可以像使用Form类一样自定义局部钩子方法和全局钩子方法来实现自定义的校验规则。

    如果我们不重写具体字段并设置validators属性的话,ModelForm是按照模型中字段的validators来校验的。

 

 

  save()方法

    每个ModelForm还具有一个save()方法。 这个方法根据表单绑定的数据创建并保存数据库对象。 ModelForm的子类可以接受现有的模型实例作为关键字参数instance;如果提供此功能,则save()将更新该实例。 如果没有提供,save() 将创建模型的一个新实例

views.py中新增书籍

def add_book(request):
    '''
    新增书籍接口
    :param request:
    :return:
    '''
    if request.method == 'GET':
        book_mode_form = BookModelForm()
        return render(request, 'model_form_addBook.html', {'book_mode_form':book_mode_form})
    else:
        book_mode_form = BookModelForm(request.POST)

        if book_mode_form.is_valid():
            #保存数据
            book_mode_form.save()
            return redirect('books')
        else:
            return render(request, 'model_form_addBook.html',{'book_mode_form': book_mode_form})

views.py中编辑书籍

def edit_book(request,book_id):
    '''
    修改书籍
    :param request:
    :param book_id: 书籍id
    :return:
    '''
    book_obj = models.Book.objects.filter(nid=book_id)
    book_obj = book_obj.first()
    if request.method =='GET':

        book_model_form_obj= BookModelForm(instance=book_obj)
        return render(request,'model_form_editBook.html',{'book_model_form_obj':book_model_form_obj,'book_obj':book_obj})
    else:
        # 注意:如果没有指定或者添加instance的话,默认会创建一条新的数据。不会去修改原来的数据。create操作
            # 如果instance指定了就会修改指定数据的值。instance=book_obj修改book_obj对象的数据。 update操作
        book_model_form_obj = BookModelForm(request.POST,instance=book_obj)
        if book_model_form_obj.is_valid():
            book_model_form_obj.save()
            return redirect('books')
        else:
            return render(request, 'model_form_editBook.html',{'book_model_form_obj': book_model_form_obj, 'book_obj': book_obj})

 

  下面看一下我们图书管理的代码吧

  urls.py文件代码

urlpatterns = [
    url(r'^admin/', admin.site.urls),

    #查看所有数据接口 ,给接口可以起个别名
    url(r'^books/', books,name='books'),

    #新增书籍
    url(r'^add_book/', add_book,name='addBook'),

    #编辑书籍接口
    url(r'^edit_book/(\d+)/$', edit_book,name='editBook'),
                    ]

  views.py文件内容

from django.core.exceptions import ValidationError
#ModelForm形式
class BookModelForm(forms.ModelForm):
    #可以重写modle表定义的规则
    # title = forms.CharField(max_length=15,min_length=6)

    class Meta:
        model = models.Book
        #加载所有字段
        # fields = '__all__'
        #单独指定需要加载的字段:只加载title字段
        # fields = ['title']
        #排除那些字段:排除title字段加载book表中其他所有字段
        exclude = ['comment','good']

        labels = {
            'title':'书籍名称',
            'publishDate': '出版日期',
            'price': '价格',
            'publishs':'出版社',
            'authors': '作者',
        }
        #给字段设置样式
        widgets = {
            'publishDate': forms.widgets.TextInput(attrs={'type':'date'})
        }

        error_messages={
            'title':{'required':'不能为空'},
            'price': {'required': '不能为空'},
            'publishDate': {'required': '不能为空'},
            'publishs': {'required': '不能为空'},
            'authors': {'required': '不能为空'},
        }

    #定义局部钩子
    def clean_title(self):
        value = self.cleaned_data.get('title')
        if '666' in value:
            raise ValidationError('输入敏感字符')
        else:
            return value

    #全局钩子
    def clean(self):
        pass

    #批量添加样式
    def __init__(self,*args,**kwargs):
        super().__init__(*args,**kwargs)
        for field in self.fields.values():
            field.widget.attrs.update({'class':'form-control'})



def add_book(request):
    '''
    新增书籍接口
    :param request:
    :return:
    '''
    if request.method == 'GET':
        book_mode_form = BookModelForm()
        return render(request, 'model_form_addBook.html', {'book_mode_form':book_mode_form})
    else:
        book_mode_form = BookModelForm(request.POST)

        if book_mode_form.is_valid():
            #保存数据
            book_mode_form.save()
            return redirect('books')
        else:
            return render(request, 'model_form_addBook.html',{'book_mode_form': book_mode_form})


def edit_book(request,book_id):
    '''
    修改书籍
    :param request:
    :param book_id: 书籍id
    :return:
    '''
    book_obj = models.Book.objects.filter(nid=book_id)
    book_obj = book_obj.first()
    if request.method =='GET':

        book_model_form_obj= BookModelForm(instance=book_obj)
        return render(request,'model_form_editBook.html',{'book_model_form_obj':book_model_form_obj,'book_obj':book_obj})
    else:
        # 注意:如果没有指定或者添加instance的话,默认会创建一条新的数据。不会去修改原来的数据。create操作
            # 如果instance指定了就会修改指定数据的值。instance=book_obj修改book_obj对象的数据。 update操作
        book_model_form_obj = BookModelForm(request.POST,instance=book_obj)
        if book_model_form_obj.is_valid():
            book_model_form_obj.save()
            return redirect('books')
        else:
            return render(request, 'model_form_editBook.html',{'book_model_form_obj': book_model_form_obj, 'book_obj': book_obj})

  books.html文件

{% load static %}

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <!--      告诉浏览器安装IE的最高版本渲染浏览器-->
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <!--      整个网站和等比缩放,目的兼容:移动端,web端。可以完整访问网站-->
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <title>书籍列表页</title>
    <!-- Bootstrap  引入文件 -->
    <link href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}" rel="stylesheet">
    <style>
        table td,th{
            text-align: center;
        }
    </style>
</head>
<body>
<div class="container-fluid">
    <div class="row">
        <div class="col-md-8 col-md-offset-2"><h1>查看书籍</h1></div>
    </div>
</div>

<div class="container">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <div>
{#                <a href="/add_book/" class="btn btn-primary">添加书籍</a>#}
                <a href="{% url 'addBook' %}" class="btn btn-primary">添加书籍</a>
            </div>
            <table class="table table-hover table-striped">
                <thead>
                    <tr>
                        <th>编号</th>
                        <th>书籍名称</th>
                        <th>价格</th>
                        <th>出版日期</th>
                        <th>出版社</th>
                        <th>作者</th>
                        <th>操作</th>
                    </tr>
                </thead>
                <tbody>
                    {% for book in all_books %}
                        <tr>
                            <td>{{ forloop.counter }}</td>
                            <td>{{ book.title }}</td>
                            <td>{{ book.price }}</td>
                            <td>{{ book.publishDate|date:'Y-m-d' }}</td>
                            <td>{{ book.publishs.name }}</td>
{#                            <td>#}
{#                                {% for author in  book.authors.all %}#}
{#                                    {{ author.name }}#}
{#                                    {% if forloop.last %}#}
{##}
{#                                    {% else %}#}
{#                                        ,#}
{#                                    {% endif %} #}
{#                                {% endfor %}#}
{#                            </td>#}
                            <td>{{ book.get_author_name }}</td>
                            <td>
                                <a href="{% url 'editBook' book.nid %}" class="btn btn-warning btn-sm">编辑</a>
                                <a href="{% url 'deleteBook' book.nid %}" class="btn btn-danger btn-sm">删除</a>
                            </td>
                        </tr>
                    {% endfor %}
                </tbody>
            </table>

        </div>
    </div>
</div>
</body>
</html>

  model_form_addBook.html

{% load static %}

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <!--      告诉浏览器安装IE的最高版本渲染浏览器-->
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <!--      整个网站和等比缩放,目的兼容:移动端,web端。可以完整访问网站-->
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <title>新增页面</title>
    <!-- Bootstrap  引入文件 -->
    <link href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}" rel="stylesheet">

</head>
<body>
    <div class="container-fluid">
        <div class="row">
            <div class="col-md-8 col-md-offset-2"><h1>新增书籍</h1></div>
        </div>
    </div>

    <div class="container">
        <div class="row">
            <div class="col-md-8 col-md-offset-2">
    {#            novalidate这个字段的意思不让浏览器给我校验,我直接进行校验#}
                <form action="{% url 'addBook' %}" method="post" novalidate>
                    {% csrf_token %}
                    {% for bookform in book_mode_form %}
                        <div class="form-group">
                            <label for="{{ bookform.id_for_label }}">{{ bookform.label }}</label>
                            {{ bookform }}
                            <div style="color: red; font-size: 12px;">{{ bookform.errors.0 }}</div>
                            </div>

                    {% endfor %}

                    <button class="btn btn-success pull-right">提交</button>
                </form>
            </div>
        </div>
    </div>

</body>
</html>

  model_form_editBook.html

{% load static %}

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <!--      告诉浏览器安装IE的最高版本渲染浏览器-->
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <!--      整个网站和等比缩放,目的兼容:移动端,web端。可以完整访问网站-->
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <title>编辑页面</title>
    <!-- Bootstrap  引入文件 -->
    <link href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}" rel="stylesheet">

</head>
<body>
    <div class="container-fluid">
        <div class="row">
            <div class="col-md-8 col-md-offset-2"><h1>编辑书籍</h1></div>
        </div>
    </div>

    <div class="container">
        <div class="row">
            <div class="col-md-8 col-md-offset-2">
    {#            novalidate这个字段的意思不让浏览器给我校验,我直接进行校验#}
                <form action="{% url 'editBook' book_obj.nid %}" method="post" novalidate>
                    {% csrf_token %}
                    {% for bookform in book_model_form_obj %}
                            <div class="form-group">
                             <label for="{{ bookform.id_for_label }}">{{ bookform.label }}</label>
                            {{ bookform }}
                            <div style="color: red; font-size: 12px;">{{ bookform.errors.0 }}</div>
                            </div>
                    {% endfor %}
                    <button class="btn btn-success pull-right">提交</button>
                </form>
            </div>
        </div>
    </div>

</body>
</html>

  models文件

from django.db import models

# Create your models here.

#作者表
class Author(models.Model): #比较常用的信息放到这个表里面

    name=models.CharField( max_length=32)
    age=models.IntegerField()
    # 与AuthorDetail建立一对一的关系,一对一的这个关系字段写在两个表的任意一个表里面都可以
    authorDetail=models.OneToOneField(to="AuthorDetail",to_field="id",on_delete=models.CASCADE) #就是foreignkey+unique,只不过不需要我们自己来写参数了,并且orm会自动帮你给这个字段名字拼上一个_id,数据库中字段名称为authorDetail_id

    def __str__(self):
        return self.name

#作者详细信息表
class AuthorDetail(models.Model):#不常用的放到这个表里面
    birthday=models.DateField()
    telephone=models.CharField(max_length=32)
    addr=models.CharField( max_length=64)

    def __str__(self):
        return self.telephone

#出版社表 和 书籍表 一对多的关系
class Publish(models.Model):
    name=models.CharField( max_length=32)
    city=models.CharField( max_length=32)
    email=models.EmailField() #charField类似,EmailField做邮箱数据校验使用

    def __str__(self):
        return self.name

#多对多的表关系,我们学mysql的时候是怎么建立的,是不是手动创建一个第三张表,然后写上两个字段,每个字段外键关联到另外两张多对多关系的表,orm的manytomany自动帮我们创建第三张表,两种方式建立关系都可以,以后的学习我们暂时用orm自动创建的第三张表,因为手动创建的第三张表我们进行orm操作的时候,很多关于多对多关系的表之间的orm语句方法无法使用
#如果你想删除某张表,你只需要将这个表注销掉,然后执行那两个数据库同步指令就可以了,自动就删除了。

#书籍表
class Book(models.Model):

    nid = models.AutoField(primary_key=True)
    title = models.CharField(max_length=32)
    good = models.IntegerField(default=1)
    comment = models.IntegerField(default=1)
    publishDate=models.DateField()
    price=models.DecimalField(max_digits=5,decimal_places=2)#Decimal ,max_digits整数加小数位数,decimal_places小数位数

    # 与Publish建立一对多的关系,外键字段建立在多的一方,字段publish如果是外键字段,那么它自动是int类型
    publishs=models.ForeignKey(to="Publish",to_field="id",on_delete=models.CASCADE)
    #foreignkey里面可以加很多的参数,都是需要咱们学习的,慢慢来,to指向表,to_field指向你关联的字段,不写这个,默认会自动关联主键字段,on_delete级联删除字段名称不需要写成publish_id,orm在翻译foreignkey的时候会自动给你这个字段拼上一个_id,这个字段名称在数据库里面就自动变成了publish_id
    # 与Author表建立多对多的关系,ManyToManyField可以建在两个模型中的任意一个,自动创建第三张表,并且注意一点,你查看book表的时候,你看不到这个字段,因为这个字段就是创建第三张表的意思,不是创建字段的意思,所以只能说这个book类里面有authors这个字段属性
    authors=models.ManyToManyField(to='Author',) #注意不管是一对多还是多对多,写to这个参数的时候,最后后面的值是个字符串,不然你就需要将你要关联的那个表放到这个表的上面

    def __str__(self):
        return self.title


    def get_author_name(self):
        '''
        获取作者列表
        :return:
        '''
        author_list = self.authors.all()
        author_name_list = [author.name for author in author_list]
        ret = ','.join(author_name_list)

        return ret

 

posted @ 2021-04-21 20:08  西西cc  阅读(65)  评论(0编辑  收藏  举报