Django框架 之 form组件

浏览目录

  • Form介绍

    • 普通的登录
    • 使用form组件
  • Form详情
    • 常用字段
    • 校验
    • 进阶
  • 使用Django Form流程

一、Form介绍

我们之前在HTML页面中利用form表单向后端提交数据时,都会写一些获取用户输入的标签并且用form标签把它们包起来。

与此同时我们在好多场景下都需要对用户的输入做校验,比如校验用户是否输入,输入的长度和格式等正不正确。如果用户输入的内容有错误就需要在页面上相应的位置显示显示对应的错误信息.。

Django form组件就实现了上面所述的功能。

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

  • 生成页面可用的HTML标签
  • 对用户提交的数据进行校验
  • 保留上次输入内容
  • 自动生成错误信息
  • 自动创建input标签并设置样式

1、普通的登录

views.py


1

2

3

4

5

6

7

8

9

10

def login(request):

    error_msg = ""

    if request.method == "POST":

        username = request.POST.get("username")

        pwd = request.POST.get("pwd")

        if username == yaya" and pwd == "123456":

            return HttpResponse("OK")

        else:

            error_msg = "用户名或密码错误"

    return render(request, "login.html", {"error_msg": error_msg}) 

login.html


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <meta http-equiv="x-ua-compatible" content="IE=edge">

  <meta name="viewport" content="width=device-width, initial-scale=1">

  <title>login</title>

  <style>

    .error {

      color: red;

    }

  </style>

</head>

<body>

<form action="/login/" method="post">

  {% csrf_token %}

  <p>

    <label for="username">用户名</label>

    <input type="text" name="username" id="username">

  </p>

  <p>

    <label for="pwd">密码</label>

    <input type="password" name="pwd" id="pwd">

    <span class="error"></span>

  </p>

  <p>

    <input type="submit">

    <span class="error">{{ error_msg }}</span>

  </p>

</form>

</body>

</html>  

2、使用form组件

先定义好一个LoginForm类。

views.py


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

class LoginForm(forms.Form):

    username = forms.CharField(min_length=8, label="用户名")

    pwd = forms.CharField(min_length=6, label="密码")

def login2(request):

    error_msg = ""

    form_obj = LoginForm()

    if request.method == "POST":

        form_obj = LoginForm(request.POST)

        if form_obj.is_valid():

            username = form_obj.cleaned_data.get("username")

            pwd = form_obj.cleaned_data.get("pwd")

            if username == "yaya" and pwd == "123456":

                return HttpResponse("OK")

            else:

                error_msg = "用户名或密码错误"

    return render(request, "login2.html", {"form_obj": form_obj, "error_msg": error_msg})

login.html


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <meta http-equiv="x-ua-compatible" content="IE=edge">

  <meta name="viewport" content="width=device-width, initial-scale=1">

  <title>login</title>

  <style>

    .error {

      color: red;

    }

  </style>

</head>

<body>

<form action="/login/" method="post" novalidate>

  {% csrf_token %}

  <p>

    {{ form_obj.username.label }}

    {{ form_obj.username }}

    <span class="error">{{ form_obj.username.errors.0 }}</span>

  </p>

  <p>

    {{ form_obj.pwd.label }}

    {{ form_obj.pwd }}

    <span class="error">{{ form_obj.pwd.errors.0 }}</span>

  </p>

  <p>

    <input type="submit">

    <span class="error">{{ error_msg }}</span>

  </p>

</form>

</body>

</html>

看网页效果发现 也验证了form的功能:
? 前端页面是form类的对象生成的                                      -->生成HTML标签功能
? 当用户名和密码输入为空或输错之后 页面都会提示        -->用户提交校验功能
? 当用户输错之后 再次输入 上次的内容还保留在input框   -->保留上次输入内容  

二、Form详情

1、常用字段

initial

初始值,input框里面的初始值。


1

2

3

4

5

6

7

class LoginForm(forms.Form):

    username = forms.CharField(

        min_length=8,

        label="用户名",

        initial="张三"  # 设置默认值

    )

    pwd = forms.CharField(min_length=6, label="密码")  

error_messages

重写错误信息。


1

2

3

4

5

6

7

8

9

10

11

12

class LoginForm(forms.Form):

    username = forms.CharField(

        min_length=8,

        label="用户名",

        initial="张三",

        error_messages={

            "required""不能为空",

            "invalid""格式错误",

            "min_length""用户名最短8位"

        }

    )

    pwd = forms.CharField(min_length=6, label="密码")  

password

input框的type转换为password类型。


1

2

3

4

5

6

7

class LoginForm(forms.Form):

    ...

    pwd = forms.CharField(

        min_length=6,

        label="密码",

        widget=forms.widgets.PasswordInput(attrs={‘class‘‘c1‘}, render_value=True)

    )

radioSelect

单radio值为字符串。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

class LoginForm(forms.Form):

    username = forms.CharField(

        min_length=8,

        label="用户名",

        initial="张三",

        error_messages={

            "required""不能为空",

            "invalid""格式错误",

            "min_length""用户名最短8位"

        }

    )

    pwd = forms.CharField(min_length=6, label="密码")

    gender = forms.fields.ChoiceField(

        choices=((1"男"), (2"女"), (3"保密")),

        label="性别",

        initial=3,

        widget=forms.widgets.RadioSelect

    )

单选Select

单选的选择框


1

2

3

4

5

6

7

8

class LoginForm(forms.Form):

    ...

    hobby = forms.fields.ChoiceField(

        choices=((1"篮球"), (2"足球"), (3"双色球"), ),

        label="爱好",

        initial=3,

        widget=forms.widgets.Select

    )  

多选Select

多选的选择框


1

2

3

4

5

6

7

8

class LoginForm(forms.Form):

    ...

    hobby = forms.fields.MultipleChoiceField(

        choices=((1"篮球"), (2"足球"), (3"双色球"), ),

        label="爱好",

        initial=[13],

        widget=forms.widgets.SelectMultiple

    ) 

单选checkbox

单选下拉框


1

2

3

4

5

6

7

class LoginForm(forms.Form):

    ...

    keep = forms.fields.ChoiceField(

        label="是否记住密码",

        initial="checked",

        widget=forms.widgets.CheckboxInput

    )  

多选checkbox

多选下拉框


1

2

3

4

5

6

7

8

class LoginForm(forms.Form):

    ...

    hobby = forms.fields.MultipleChoiceField(

        choices=((1"篮球"), (2"足球"), (3"双色球"),),

        label="爱好",

        initial=[13],

        widget=forms.widgets.CheckboxSelectMultiple

    )

关于choice的注意事项:

在使用选择标签时,需要注意choices的选项可以从数据库中获取,但是由于是静态字段 ***获取的值无法实时更新***,那么需要自定义构造方法从而达到此目的。

方式一:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

from django.forms import Form

from django.forms import widgets

from django.forms import fields

 

class MyForm(Form):

 

    user = fields.ChoiceField(

        # choices=((1, ‘上海‘), (2, ‘北京‘),),

        initial=2,

        widget=widgets.Select

    )

 

    def __init__(self*args, **kwargs):

        super(MyForm,self).__init__(*args, **kwargs)

        # self.fields[‘user‘].widget.choices = ((1, ‘上海‘), (2, ‘北京‘),)

        # 或

        self.fields[‘user‘].widget.choices = models.Classes.objects.all().values_list(‘id‘,‘caption‘)  

方式二:  


1

2

3

4

5

6

7

8

from django import forms

from django.forms import fields

from django.forms import models as form_model

 

class FInfo(forms.Form):

    authors = form_model.ModelMultipleChoiceField(queryset=models.NNewType.objects.all())

    # authors = form_model.ModelChoiceField(queryset=models.NNewType.objects.all())

Field
    required=True,               是否允许为空
    widget=None,                 HTML插件
    label=None,                  用于生成Label标签或显示内容
    initial=None,                初始值
    help_text=‘‘,                帮助信息(在标签旁边显示)
    error_messages=None,         错误信息 {‘required‘: ‘不能为空‘, ‘invalid‘: ‘格式错误‘}
    show_hidden_initial=False,   是否在当前插件后面再加一个隐藏的且具有默认值的插件(可用于检验两次输入是否一直)
    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类型

  

2、校验

方式一


1

2

3

4

5

6

7

8

9

from django.forms import Form

from django.forms import widgets

from django.forms import fields

from django.core.validators import RegexValidator

 

class MyForm(Form):

    user = fields.CharField(

        validators=[RegexValidator(r‘^[0-9]+$‘‘请输入数字‘), RegexValidator(r‘^159[0-9]+$‘‘数字必须以159开头‘)],

    )  

方式二


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

import re

from django.forms import Form

from django.forms import widgets

from django.forms import fields

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‘邮箱‘}))  

3、进阶

应用Bootstrap样式

<!DOCTYPE html>
<html lang="en">
<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>login</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_obj.username.id_for_label }}"
               class="col-md-2 control-label">{{ form_obj.username.label }}</label>
        <div class="col-md-10">
          {{ form_obj.username }}
          <span class="help-block">{{ form_obj.username.errors.0 }}</span>
        </div>
      </div>
      <div class="form-group">
        <label for="{{ form_obj.pwd.id_for_label }}" class="col-md-2 control-label">{{ form_obj.pwd.label }}</label>
        <div class="col-md-10">
          {{ form_obj.pwd }}
          <span class="help-block">{{ form_obj.pwd.errors.0 }}</span>
        </div>
      </div>
      <div class="form-group">
      <label class="col-md-2 control-label">{{ form_obj.gender.label }}</label>
        <div class="col-md-10">
          <div class="radio">
            {% for radio in form_obj.gender %}
              <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">注册</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>

批量添加样式

可通过重写form类的init方法来实现。

class LoginForm(forms.Form):
    username = forms.CharField(
        min_length=8,
        label="用户名",
        initial="张三",
        error_messages={
            "required": "不能为空",
            "invalid": "格式错误",
            "min_length": "用户名最短8位"
        }
    ...

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

三、使用Django Form流程

1、urls.py配置关系映射

2、views.py中创建Form类,定义字段的校验条件,在映射关系中实例化Form类,进行逻辑处理,将Form对象通过响应Response返回页面中

3、页面中调用实例化Form对象进行操作

form对象渲染样式:

方式一


1

2

3

{{ form.as_table }} 以表格的形式将它们渲染在<tr> 标签中

{{ form.as_p }} 将它们渲染在<p> 标签中

{{ form.as_ul }} 将它们渲染在<li> 标签中 

整体导入,应用不方便没有特殊样式。

方式二


1

{{form.user}}  

优点:使用方便,随意修改特殊样式;

缺点:如果标签太多,操作会很麻烦。

方式三


1

2

3

4

5

6

7

8

{% for field in form %}

 <div>

      <label for="">{{ field.label }}</label>

      <div>

       {{ field }}

      </div>

      </div>

 {% endfor %}

for循环,代码简捷,操作方便,效率高,但不便添加特殊样式。 

 

示例:注册

from app01 import views

urlpatterns = [
    url(r‘^admin/‘, admin.site.urls),
    url(r‘^register/‘, views.register),
]

#models里面建表
class UserInfo(models.Model):
    user = models.CharField(max_length=32)
    pwd = models.CharField(max_length=32)

    def __str__(self):
        return self.user

#在项目里新建forms.py

from django import forms

class RegisterForm(forms.Form):

    user = forms.CharField(
        min_length=6,
        label="用户名",
        error_messages={
            "required": "用户名不能为空",
            "min_length": "用户名不能小于6位",
        }
    )

    pwd = forms.CharField(
        label="密码",
        min_length=6,
        error_messages={
            "required": "密码不能为空",
            "min_length": "密码不能小于6位",
        },
        widget=forms.widgets.PasswordInput()
    )

from django.shortcuts import render, HttpResponse
from app01 import forms, models
# Create your views here.

def register(request):
    # 实例化一个form示例
    form_obj = forms.RegisterForm()
    if request.method == "POST":
        # 把POST提交过来的数据传到form表单里
        form_obj = forms.RegisterForm(request.POST)
        # 调用实例化对象的is_valid()方法,帮我做校验!
        if form_obj.is_valid():
            # 校验通过
            # form_obj.cleaned_data <-- 所有有效的数据都存放在cleaned_data属性中
            # user = form_obj.cleaned_data.get("user")
            # pwd = form_obj.cleaned_data.get("pwd")
            # 调用ORM,在数据库中创建新的用户
            # models.UserInfo.objects.create(user=user, pwd=pwd)
            # print(form_obj.cleaned_data)
            models.UserInfo.objects.create(**form_obj.cleaned_data)
            return HttpResponse("O98K")
        # else:
        #     # 校验失败
        #     # 此时,form_obj.errors 里面保存了报错的信息
        #     return render(request, "register.html", {"form_obj": form_obj})
    return render(request, "register.html", {"form_obj": form_obj})

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>

<h1>欢迎注册!</h1>
<form action="" method="post" novalidate>
    {% csrf_token %}
    <p>
        <label for="{{ form_obj.user.id_for_label }}">{{ form_obj.user.label }}</label>
        {{ form_obj.user }}
        <span>{{ form_obj.user.errors.0 }}</span>
    </p>

    <p>
        <label for="{{ form_obj.pwd.id_for_label }}">{{ form_obj.pwd.label }}</label>
        {{ form_obj.pwd }}
        <span>{{ form_obj.pwd.errors.0 }}</span>
    </p>

    <p>
        <input type="submit" value="注册">
    </p>

</form>
</body>
</html>

原文地址:https://www.cnblogs.com/hanbowen/p/9567052.html

时间: 2024-10-29 03:22:01

Django框架 之 form组件的相关文章

Django框架之Form组件

新手上路 From组件的主要功能: 1,对form表单提交的数据进行校验. 内部基于正则进行匹配 2,from表单提交保存上次提交的数据. 生成HTML标签 初入大荒 1,创建Form类 class StudentForm(Form): #字段名,最好跟数据库内的字段名一样,好操作 #如CharField,内部包含了正则表达式, name = fields.CharField(max_length=12, min_length=2, required=True, widget=widgets.T

Django框架 之 form组件的钩子

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 def foo(request):     if request.method=="POST":         regForm=Reg

Django 框架篇(九): Django中的Form 组件

Django中的Form组件,帮我们处理了 form表单中的校验, 和错误提示的处理: 主要功能: 生成页面可用的HTML标签 对用户提交的数据进行校验 保留上次输入内容 使用form组件实现注册功能 1.  定义一个类, 继承django中的 forms.Form 代码实例: from django import forms # 按照Django form组件的要求自己写一个类 class RegForm(forms.Form): name = forms.CharField(label="用

Django框架 之 modelform组件

浏览目录 创建mldelform 添加记录 编辑记录 Django框架中的modelform组件 通过名字我们可以看出来,这个组件的功能就是把model和form组合起来. 下面我们来看一个例子: 比如我们的数据库中有这样一张学生表,字段有姓名,年龄,爱好,邮箱,电话,住址,注册时间等等一大堆信息,现在让你写一个创建学生的页面,你的后台应该怎么写呢?首先我们会在前端一个一个罗列出这些字段,让用户去填写,然后我们从后天一个一个接收用户的输入,创建一个新的学生对象,保存其实,重点不是这些,而是合法性

django补充和form组件

Model常用操作: - 参数:filter - all,values,values_list [obj(id,name,pwd,email),obj(id,name,pwd,email),] models.UserInfo.objects.all() [obj(id,name,email)] # pwd未取值 data_list = models.UserInfo.objects.all().only('name','email') for item in data_list: item.id

Django框架之Forms组件(基于注册功能)

1.注册功能分析 注册功能 1.渲染前端标签获取用户输入 >>> 渲染标签 2.获取用户输入传递到后端校验 >>> 校验数据 3.校验未通过展示错误信息 >>> 展示信息 2.使用 forms 组件实现注册功能 1.校验数据 校验规则:form组件校验数据的规则从上往下依次取值校验    校验通过的放到cleaned_data    校验失败的放到errors from django import forms class MyForm(forms.Fo

Django—分页器与form组件

目录 分页器 现成分页器代码 如何使用 后端 前端 form组件 form 校验组件 使用校验组件与钩子函数 三种渲染方式 正则校验* 分页器 现成分页器代码 只需要会使用即可. 在Django项目下新建utils文件夹,新建一个py文件,放入封装好的分页器代码. class Pagination(object): def __init__(self, current_page, all_count, per_page_num=10, pager_count=11): """

Django框架 之 Form表单和Ajax上传文件

浏览目录 Form表单上传文件 Ajax上传文件 伪造Ajax上传文件 Form表单上传文件 html <h3>form表单上传文件</h3> <form action="/upload_file/" method="post" enctype="multipart/form-data"> <p><input type="file" name="upload_fi

用Django内置form组件实现注册

HTML页面代码块: 1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 6 <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css"> 7 <title>首页</title&