多对多的三种创建方式、Form组件、Cookie与Session

一、全自动(推荐使用*)

优点:

不需要手动创建第三张表

缺点:

由于第三张表不是手动创建的,

也就意味着第三张表字段是固定的,

无法做扩展,扩张性差

 class Book(models.Model):
     title = models.CharField(max_length=32)
     price = models.DecimalField(max_digits=8,decimal_places=2)
     authors = models.ManyToManyField(to='Author')

class Author(models.Model):
   name = models.CharField(max_length=32)

二、纯手动(了解即可)

自己创建第三张表

优点:

第三张表可以任意的扩展字段,扩展性好

缺点:

orm查询不方便

 class Book(models.Model):
     title = models.CharField(max_length=32)
     price = models.DecimalField(max_digits=8,decimal_places=2)


 class Author(models.Model):
     name = models.CharField(max_length=32)


 class Book2Author(models.Model):
     book = models.ForeignKey(to='Book')
     author = models.ForeignKey(to='Author')
     create_time = models.DateField(auto_now_add=True)

三、半自动(推荐使用***)

优点:

结合了全自动和纯手动的两个优点

class Book(models.Model):
    title = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=8,decimal_places=2)
    authors = models.ManyToManyField(to='Author',through='Book2Author',through_fields=('book','author'))
    # through 告诉django orm 书籍表和作者表的多对多关系是通过Book2Author来记录的
    # through_fields 告诉django orm记录关系时用过Book2Author表中的book字段和author字段来记录的
    """
    多对多字段的
    add
    set
    remove
    clear不支持
    """


class Author(models.Model):
    name = models.CharField(max_length=32)
    # books = models.ManyToManyField(to='Book', through='Book2Author', through_fields=('author', 'book'))


class Book2Author(models.Model):
    book = models.ForeignKey(to='Book')
    author = models.ForeignKey(to='Author')
    create_time = models.DateField(auto_now_add=True)

二、forms组件

form组件的主要功能如下:

  • 生成页面可用的HTML标签
  • 对用户提交的数据进行校验
  • 保留上次输入内容

1.手动实现注册功能

当用户的用户名包含啦啦啦,提示敏感字符,请重试

当用户的密码短于3位,提示数字太短,请重输

def login(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) < 3:
            errors['password'] = '数字太短,请重输'
    return render(request,'login.html',locals())
views.py
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="jQuery-3.4.1.js"></script>
</head>
<body>
<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>
    <p><input type="submit"></p>
</form>
</body>
</html>
login.html

2.froms组件基本用法

1.将需要校验的数据 以字典的方式传递给自定义的类 实例化产生对象
form_obj = views.LoginForm({'username':'tank','password':'123','email':'123456'})
2.如何查看数据是否全部合法
form_obj.is_valid()  # 只有所有的数据都符合要求 才会是True
False
3.如何查看错误原因
{'email': ['Enter a valid email address.']}
4.如何查看通过校验的数据
{'username': 'tank', 'password': '123'}

注意事项:

1.自定义类中所有的字段默认都是必须要传值的,

2.可以额外传入类中没有定义的字段名 forms组件不会去校验 也就意味着多传一点关系没有

3.渲染页面的三种方式

views.py代码

第一种方式

                <p>第一种渲染页面的方式(封装程度太高 一般只用于本地测试  通常不适用)</p>
                {{ form_obj.as_p }}  
                {{ form_obj.as_ul }}
                {{ form_obj.as_table }}

第二种方式

                      <p>第二种渲染页面的方式(可扩展性较高 书写麻烦)</p>
                <p>{{ form_obj.username.label }}{{ form_obj.username }}</p>
                <p>{{ form_obj.password.label }}{{ form_obj.password }}</p>

第二种方式

                <p>第三种渲染页面的方式(推荐)</p>
                {% for foo in form_obj %}
                    <p>{{ foo.label }}{{ foo }}</p>
                {% endfor %}

 注意事项

1.forms组件在帮你渲染页面的时候 只会渲染获取用户输入的标签  提交按钮需要你手动添加

2.input框的label注释  不指定的情况下 默认用的类中字段的首字母大写

校验数据的时候可以前后端都校验 做一个双重的校验
但是前端的校验可有可无 而后端的校验则必须要有,因为前端的校验可以通过爬虫直接避开

前端取消浏览器校验功能,form标签指定novalidate属性即可

4.展示错误信息

前端代码

后端代码

 

5.字段校验

RegexValidator验证器

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开头')],
    )

6.钩子函数(HOOK)

在Form类中定义钩子函数,来实现自定义的验证功能。

局部钩子

针对某一个字段做额外的校验),校验用户名中不能包含000 一旦包含 提示

 浏览器显示页面

全局钩子

针对多个字段做额外的校验,校验用户两次密码是否一致

7.forms组件其他字段及操作方式

label:注释信息

initial:默认值

required:是否必填

error_messages:报错信息

widget:控制标签属性和样式  widget=widgets.PasswordInput()

单选radio

     gender = forms.ChoiceField(
         choices=((1, ""), (2, ""), (3, "保密")),
         label="性别",
         initial=3,
         widget=forms.widgets.RadioSelect()

单选Select

class LoginForm(forms.Form):
    ...
    hobby = forms.ChoiceField(
        choices=((1, "篮球"), (2, "足球"), (3, "双色球"), ),
        label="爱好",
        initial=3,
        widget=forms.widgets.Select()
    )

多选Select

复制代码
复制代码
class LoginForm(forms.Form):
    ...
    hobby = forms.MultipleChoiceField(
        choices=((1, "篮球"), (2, "足球"), (3, "双色球"), ),
        label="爱好",
        initial=[1, 3],
        widget=forms.widgets.SelectMultiple()
    )
复制代码
复制代码

单选checkbox

复制代码
复制代码
class LoginForm(forms.Form):
    ...
    keep = forms.ChoiceField(
        label="是否记住密码",
        initial="checked",
        widget=forms.widgets.CheckboxInput()
    )
复制代码
复制代码

多选checkbox

复制代码
复制代码
class LoginForm(forms.Form):
    ...
    hobby = forms.MultipleChoiceField(
        choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
        label="爱好",
        initial=[1, 3],
        widget=forms.widgets.CheckboxSelectMultiple()
    )
复制代码
复制代码

三、Cookie与Session

Cookie的由来

大家都知道HTTP协议是无状态的。

无状态的意思是每次请求都是独立的,它的执行情况和结果与前面的请求和之后的请求都无直接关系,它不会受前面的请求响应情况直接影响,也不会直接影响后面的请求响应情况。

一句有意思的话来描述就是人生只如初见,对服务器来说,每次的请求都是全新的。

状态可以理解为客户端和服务器在某次会话中产生的数据,那无状态的就以为这些数据不会被保留。会话中产生的数据又是我们需要保存的,也就是说要“保持状态”。因此Cookie就是在这样一个场景下诞生。

cookie

什么是Cookie

大家都知道HTTP协议是无状态的。

无状态的意思是每次请求都是独立的,它的执行情况和结果与前面的请求和之后的请求都无直接关系,它不会受前面的请求响应情况直接影响,也不会直接影响后面的请求响应情况。

一句有意思的话来描述就是人生只如初见,对服务器来说,每次的请求都是全新的。

状态可以理解为客户端和服务器在某次会话中产生的数据,那无状态的就以为这些数据不会被保留。会话中产生的数据又是我们需要保存的,也就是说要“保持状态”。因此Cookie就是在这样一个场景下诞生。

由于http协议是无状态的 无法记录用户状态

cookie就是保存在客户端浏览器上的键值对
工作原理:当你登陆成功之后 浏览器上会保存一些信息
下次再访问的时候 就会带着这些信息去访问服务端 服务端通过这些信息来识别出你的身份

cookie虽然是写在客户端浏览器上的 但是是服务端设置的
浏览器可以选择不服从命令 禁止写cookie

Django中操作Cookie

django返回给客户端浏览器的都必须是HttpResponse对象

设置Cookie

obj1.set_cookie('k1','v1')

获取Cookie

request.COOKIE.get()

删除Cookie

obj1.delete_cookie("k1")

设置超时时间

max_age=None, 超时时间

expires=None, 超时时间(IE requires expires, so set it if hasn't been already.)

Cookie版登陆校验

def lg(request):
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        if username == 'jason' and password == '123':
            # 先获取url中get请求携带的参数
            old_url = request.GET.get('next')
            # 判断用户是直接访问的登陆页面 还是从别的页面的调过来
            if old_url:
                obj = redirect(old_url)
            else:
                # 如果用户直接访问的登陆页面 那么登陆完成之后 直接跳到网站的首页
                obj = redirect('/home/')
            obj.set_cookie('name','jason',max_age=30)  # 浏览器上就会保存键值对name:jason
            return obj
    return render(request,'lg.html')



from functools import wraps


def login_auth(func):
    @wraps(func)
    def inner(request,*args,**kwargs):
        # 从request中获取cookie
        # print(request.path)
        # print(request.get_full_path())
        target_url = request.get_full_path()
        if request.COOKIES.get('name'):
            res = func(request,*args,**kwargs)
            return res
        else:
            return redirect('/lg/?next=%s'%target_url)
    return inner


@login_auth
def home(request):
    return HttpResponse('home页面 只有登录了才能看')



@login_auth
def index(request):
    return HttpResponse("index页面 只有登录了才能访问")

@login_auth
def xxx(request):
    return HttpResponse('xxx页面 登陆之后才能看')
cookie版登陆校验

Session

Session的由来

Cookie虽然在一定程度上解决了“保持状态”的需求,但是由于Cookie本身最大支持4096字节,以及Cookie本身保存在客户端,可能被拦截或窃取,因此就需要有一种新的东西,它能支持更多的字节,并且他保存在服务器,有较高的安全性。这就是Session。

问题来了,基于HTTP协议的无状态特征,服务器根本就不知道访问者是“谁”。那么上述的Cookie就起到桥接的作用。

我们可以给每个客户端的Cookie分配一个唯一的id,这样用户在访问时,通过Cookie,服务器就知道来的人是“谁”。然后我们再根据不同的Cookie的id,在服务器上保存一段时间的私密资料,如“账号密码”等等。

总结而言:Cookie弥补了HTTP无状态的不足,让服务器知道来的人是“谁”;但是Cookie以文本的形式保存在本地,自身安全性较差;所以我们就通过Cookie识别不同的用户,对应的在Session里保存私密的信息以及超过4096字节的文本。

另外,上述所说的Cookie和Session其实是共通性的东西,不限于语言和框架。

什么是Session

session就是保存在服务器上的键值对
            session虽然是保存在服务器上的键值对
            但是它是依赖于cookie工作的
            
            服务端返回给浏览器一个随机的字符串
            浏览器以键值对的形式保存
            sessionid:随机字符串
            
            浏览器在访问服务端的时候  就会将随机字符串携带上
            后端获取随机串与后端的记录的做比对
                随机字符串1:数据1
                随机字符串2:数据2

设置Session

request.session['k1']

设置Session发生了三件事
1.django 内部自动生成一个随机字符串
2.将随机字符串和你要保存的数据 写入django_session表中(现在内存中生成一个缓存记录 等到经过中间件的时候才会执行)
3.将产生的随机字符串发送给浏览器写入cookie
sessionid:随机字符串

获取Session

request.session.get('k1')

获取Session发生了三件事
1.django内部会自动从请求信息中获取到随机字符串
2.拿着随机字符串去django_session表中比对
3.一旦对应上了就将对应的数据解析出来放到request.session中

 django session默认的超时时间是14天

django_session表中的一条记录针对一个浏览器

删除Session数据

request.session.delete()  # 删除的是浏览器的sessionid信息
删除当前的会话数据并删除会话的Cookie

 request.session.flush()  # 将浏览器和服务端全部删除

设置会话Session和Cookie的超时时间

request.session.set_expiry(value)

*如果value是个整数,session会在些秒数后失效。
* 如果value是个datatime或timedelta,session就会在这个时间后失效
* 如果value是0,用户关闭浏览器session就会失效。
* 如果value是None,session会依赖全局session失效策略。

Session版登陆验证

from functools import wraps


def check_login(func):
    @wraps(func)
    def inner(request, *args, **kwargs):
        next_url = request.get_full_path()
        if request.session.get("user"):
            return func(request, *args, **kwargs)
        else:
            return redirect("/login/?next={}".format(next_url))
    return inner


def login(request):
    if request.method == "POST":
        user = request.POST.get("user")
        pwd = request.POST.get("pwd")

        if user == "alex" and pwd == "alex1234":
            # 设置session
            request.session["user"] = user
            # 获取跳到登陆页面之前的URL
            next_url = request.GET.get("next")
            # 如果有,就跳转回登陆之前的URL
            if next_url:
                return redirect(next_url)
            # 否则默认跳转到index页面
            else:
                return redirect("/index/")
    return render(request, "login.html")


@check_login
def logout(request):
    # 删除所有当前请求相关的session
    request.session.delete()
    return redirect("/login/")


@check_login
def index(request):
    current_user = request.session.get("user", None)
    return render(request, "index.html", {"user": current_user})

Session版登录验证
Session版

猜你喜欢

转载自www.cnblogs.com/xiongying4/p/11581442.html
今日推荐