Django(九)

目录

Django(九)---多对多的创建方式,horm组件

多对多表创建方式

全自动

通过Django中的orm,只要定义好表类,orm就对自动创建多对多的表关系,自动建立第三张表,并且还可以通过add remove set clear对第三张表进行操作

缺点:

? 因为第三张表是自动创建的,所以该表无法扩展和自定义字段,标的扩展性较差

class Book(models.Model):
    title = models.CharField(max_length=32)
    # 多对多关系字段
    authors = models.ManyToManyField(to='Authors')
    
class Authors(models.Model):
    name = models.CharField(max_length=32)

纯手写

通过模型表类,手动创建第三张关系表

通过手动建立第三张关系表,该表中的字段个数和字段名称都可以实现自定义

缺点:

? 自定义的第三张表,不能支持orm的跨表查询,也没有正反查询的概念

class Book(models.Model):
    title = models.CharField(max_length=32)
    
class Authors(models.Model):
    name = models.CharField(max_length=32)

class Book2Authors(models.Model):
    book = models.ForeignKey(to="Book")
    author = models.ForeignKey(to="Authors")
    create_time = models.DateField(auto_now_add = True)

半自动

在全自动的基础上,手动创建第三张表,并在MangyToManyField方法内再加一些参数(through=‘手动创建的第三张表名‘,through_fields=(‘外键字段1‘,‘外键字段2‘)

? ‘外键字段1‘当前所在表的外键

? ‘外键字段2‘关联表的外键

通过该方式创建第三张表,可以自定义添加任意字段,并且支持orm查询

缺点:

? 不支持 add remove set clear方法,对第三张表进行操作

class Book(models.Model):
    title = models.CharField(max_length=32)
    # 多对多关系字段
    authors = models.ManyToManyField(to='Authors',through='Book2Author',through_fields=("book","authors"))
    """
    当你的ManyToManyField只有一个参数to的情况下 orm会自动帮你创建第三张表
    如果你加了through和through_fields那么orm就不会自动帮你创建第三张表 但是它会在内部帮你维护关系 让你能够继续使用orm的跨表查询
    through  自己指定第三张关系表
    through_fields  自己指定第三张关系表中 到底哪两个字段维护者表与表之间的多对多关系
    """

class Authors(models.Model):
    name = models.CharField(max_length=32)
    # 多对多关系字段  等价
    books = models.ManyToManyField(to='Book', through='Book2Author', through_fields=("authors","book"))

class Book2Author(models.Model):
    book = models.ForeignKey(to='Book')
    authors = models.ForeignKey(to='Authors')
    
    # 该表中可以由任意多的外键字段
    # 可以扩展任意的字段

form组件

通过form组件,对用户输入的信息进行校验

如:

  1. 用户名规定不能含有敏感词
  2. 密码不能少于3位
def register(request):
    errors = {'username':'','password':''}
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        if '哈哈' in username:
            errors['username'] = '用户名包含敏感词'
        if len(password)<4:
            errors['password'] = '密码不能少于三个'

    return render(request,'register.html',locals())
<form action="" method="post">
    <p>username:
        <input type="text" name="username">
        <span style="color: red">{{ errors.username }}</span>
    </p>
    <p>password:
        <input type="text" name="password">
        <span style="color: red">{{ errors.password }}</span>
    </p>
    <input type="submit">
    <p></p>
</form>

校验数据

使用form组件时,需要提前定义一个类

from django import forms

class MyForm(forms.Form):
    username = forms.CharField(max_length=8,min_length=3)
    # 使用CharField字段,max_length表示最多位数,min_length表示最少位数
    password = forms.IntegerField(max_value=8,min_value=3)
    # email字段 必须是邮箱格式
    email = forms.EmailField()

通过 form_obj.is_valid()方法,可以校验提交的数据是否符合条件

? 只有当数据全部符合校验规则的情况下,结果才是True

通过 form_obj.errors方法,可以获取不符合规则的字段及错误的理由

通过 form_obj.cleaned方法,可以获取校验通过的数据,返回的是一个字典

forms组件中定义的字段默认都是必须传值的,不能少传,如果少传会报错

forms组件只会校验forms类中定义的字段,如果多传了,不会有任何影响

渲染标签

<p>方式一:速度快,但是封装成程度太高,不能加标签样式</p>
{{ form_obj.as_p }}
{{ form_obj.as_ul }}
{{ form_obj.as_table }}
<p>方式二:</p> #方式二写法过于繁琐
{{ form_obj.username.label }}{{ form_obj.username }}
{{ form_obj.password.label }}{{ form_obj.password }}
{{ form_obj.email.label }}{{ form_obj.email }}
<p>方式三:</p>  #使用该方法,不管有多少个输入框,都可以通过for循环一次性渲染
{% for form in form_obj %}
    <p>{{ form.label }}{{ form }}</p>#和方式2中的对象点字段名一样
{% endfor %}

通过在自定义的MyForm中的属性值设置label的值,可以自定义前端label的是值

通过设置widget=forms.widgets.TextInput({‘class‘:‘form-control‘,键值对})的值,可以对标签进行js渲染

展示信息

使用form组件,前端会自动识别,并帮你做校验

但是前端的校验保护措施过于薄弱,因此在写项目时,需要取消前端的校验

在后端进行真正的校验

让浏览器不做校验,需要在form表单中添加一个novalidate参数

? <form action="" method="post" novalidate>

<form action="" method="post" novalidate>
    {% for form in form_obj %}
        <p>{{ form.label }}{{ form }} <span>{{ form.errors.0 }}</span></p>
    {% endfor %}
    <input type="submit">
</form>

通过{{ form.errors.0 }}可以获取后端传过来的报错信息,报错信息为英文

在后端自定义的MyForm类中,给属性值自定义error_messages值,可是是前端得到自定义的报错信息

username = forms.CharField(max_length=8,min_length=3,label='用户名',initial='默认值',
                           error_messages={
                               'max_length':'用户名最长八位',
                               'min_length':'用户名最短三位',
                               'required':'用户名不能为空'
                           },required=False,
                           widget=forms.widgets.TextInput({'class':'form-control c1 c2','username':'jason'})
                           )

内置的校验器

通过validators设置校验规则,规则一般为正则表达式

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

勾子函数

符合所有字段规则的数据,才会进行勾子函数内的校验

form组件校验通过的数据会存放在cleaned_data中

局部钩子

# 校验用户名中不能含有666     局部钩子
def clean_username(self):
    username = self.cleaned_data.get('username')
    if '666' in username:
        # 给username所对应的框展示错误信息
        # self.add_error('username','光喊666是不行的')
        raise ValidationError('到底对不对啊')
    # 将username数据返回
    return username

全局钩子

```python

校验密码 确认密码是否一致 全局钩子

def clean(self):
password = self.cleaned_data.get("password")
confirm_password = self.cleaned_data.get("confirm_password")
if not password == confirm_password:
self.add_error(‘confirm_password‘,‘两次密码不一致‘)
# 将全局的数据返回
return self.cleaned_data
```

补充知识点

在自定义类的实行中设置 initial 值,会在前端中显示初始值

设置required 属性,默认为True,为True时该输入框不能为空,设置为False,可以为空

label       input对应的提示信息
initial     input框默认值
required    默认为True控制字段是否必填
widget      给input框设置样式及属性
            widget=forms.widgets.PasswordInput({'class':'form-control c1 c2',})
            widget=forms.widgets.TextInput({'class':'form-control c1 c2',})
            widget=forms.widgets.TextInput(attrs={'class':'form-control c1 c2',})

相关推荐