# ajax结合sweetalert 1.下载源码 只需要到dist文件夹内的css和js即可 2.拷贝使用官网提供的案例代码 3.添加配置参数新增等待特效 <div class="container"> <div class="row"> <div class="col-md-8 col-md-offset-2"> <h2 class="text-center">数据展示</h2> <table class="table table-striped table-hover"> <thead> <tr> <th>主键</th> <th>姓名</th> <th>年龄</th> <th>地址</th> <th>操作</th> </tr> </thead> <tbody> {% for user_obj in user_queryset %} <tr> <td>{{ user_obj.pk }}</td> <td>{{ user_obj.name }}</td> <td>{{ user_obj.age }}</td> <td>{{ user_obj.addr }}</td> <td> <a href="#" class="btn btn-primary btn-xs">编辑</a> <a href="#" class="btn btn-danger btn-xs deleteBtn" delete_id="{{ user_obj.pk }}">删除</a> </td> </tr> {% endfor %} </tbody> </table> </div> </div> </div> <script> $('.deleteBtn').click(function () { let $deBtn = $(this); swal({ title: "你确定要删除吗?", text: "你要是真删 就准备好跑路吧!", type: "warning", showCancelButton: true, confirmButtonClass: "btn-danger", confirmButtonText: "牛逼, 准备跑路!", cancelButtonText: "怂货, 我真瞧不起你!", closeOnConfirm: false, closeOnCancel: false, showLoaderOnConfirm: true }, function(isConfirm) { if (isConfirm) { // 编写与后端交互的删除功能 let deleteId = $deBtn.attr('delete_id'); // 发送ajax请求 $.ajax({ url:'', type:'post', data:{'deleteId':deleteId}, success:function (args) { swal(args, "success"); $deBtn.parent().parent().remove() } }) } else { swal("咋又取消了", "你真的怂到家了!", "error"); } }); }) </script>
""" 钩子函数的含义其实就是在程序的执行过程中穿插额外的逻辑 校验用户名是否已存在 钩子函数之局部钩子(校验单个字段) 校验密码和确认密码是否一致 钩子函数之全局钩子(校验多个字段) """
# 校验用户名是否已存在(一次性只能勾一个人) '''钩子函数是数据经过了字段第一层参数校验之后才会执行''' def clean_name(self): # 自动生成的函数名 专门用于对name字段添加额外的校验规则 # 1.先获取用户名 name = self.cleaned_data.get('name') # 2.判断用户名是否已存在 is_exist = models.User.objects.filter(name=name) if is_exist: # 3.提示信息 self.add_error('name', '用户名已存在') # 4.最后将你勾上来的name返回回去 return name
# 校验密码与确认密码是否一致(一次性可以勾多个人) def clean(self): # 1.获取多个字段数据 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
min_length 最小长度 max_length 最大长度 label 字段名称 error_messages 错误提示 min_value 最小值 max_value 最大值 initial 默认值 validators 正则校验器
from django.core.validators import RegexValidator phone = forms.CharField( validators=[ RegexValidator(r'^[0-9]+$', '请输入数字'), RegexValidator(r'^159[0-9]+$', '数字必须以159开头')], ) widget 控制渲染出来的标签各项属性 password = forms.CharField( widget=forms.widgets.PasswordInput(attrs={'class':'form-control'}) ) forms.widgets.控制type的类型(attrs=控制各项属性:class id ...) choices 选择类型的标签内部对应关系 可以直接编写 也可以从数据库中获取 hobby = forms.MultipleChoiceField( choices=((1, "篮球"), (2, "足球"), (3, "双色球"),), ) def __init__(self, *args, **kwargs): super(MyForm,self).__init__(*args, **kwargs) self.fields['hobby'].choices = models.Classes.objects.all().values_list('id','caption')
Django内置字段如下:
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类型 ... 'UUID是根据MAC以及当前时间等创建的不重复的随机字符串'
1 为什么局部钩子要写成 clean_字段名,为什么要抛异常 2 入口在 is_valid() 3 校验流程 -先校验字段自己的规则(最大,最小,是否必填,是不是合法) -校验局部钩子函数 -全局钩子校验 4 流程 -is_valid()---》return self.is_bound and not self.errors -self.errors:方法包装成了数据数据 -一旦self._errors有值,就不进行校验了(之前调用过了) -self.full_clean():核心 self._errors = ErrorDict() if not self.is_bound: return self.cleaned_data = {} self._clean_fields() self._clean_form() self._post_clean() -self._clean_fields():核心代码,局部钩子执行位置 value = field.clean(value)# 字段自己的校验规则 self.cleaned_data[name] = value #把校验后数据放到cleaned_data if hasattr(self, 'clean_%s' % name): # 判断有没有局部钩子 value = getattr(self, 'clean_%s' % name)() #执行局部钩子 self.cleaned_data[name] = value #校验通过,把数据替换一下 # 如果 校验不通过,会抛异常,会被捕获,捕获后执行 self.add_error(name, e) - def _clean_form(self):#全局钩子执行位置 def _clean_form(self): try: #如果自己定义的form类中写了clean,他就会执行 cleaned_data = self.clean() except ValidationError as e: self.add_error(None, e)
1.from django import forms 导入模块 2.继承Form类,构造一个自己的表单类。类似于Models类,django通过model操作数据库表。Form对象这是一个表单对象,通过该对象来操纵表单处理的过程,如校验表单字段,渲染表单字段。主要就对这两方面进行操纵。 3.关于提交的表单数据的校验,提供了自定义全局和局部钩子,提供了丰富的内置Field类和其对应的widget来约束表单提交的数据。(插曲:所谓钩子,就是访问入口规定好了,我们就添加入口里面的东西就可以了) 4.局部钩子注意获取到校验值,进行校验后,符合要返回该值,不符合抛出一个指定的异常 ValidationError 异常 5.全局钩子主要用于每个空间的值都局部校验后,进行一个全局校验,比如两次密码是否一样。这样就不必在从clean_data取出来比较了。如果校验成功过,注意返回的是clean_data,失败同样抛出ValidationError异常。全局校验错误信息是存在form.errrors 的__all__的一个key对应的列表中。 6.is_valid clean_name errors 7.关于渲染表单 form为每个field提供了相对应的一个默认widget。当然也可以自定义,在定义form字段是,可以带入参数widget指定widget类或该类的实例对象。如果传入的是widget类,那么会自动实例一个默认的widget对象用于字段渲染。如果传入的是实例,就按照实例的渲染方式进行渲染。 8.表单渲染主要就是field对应的widget的作用。当然内置的多种widget都可以传入相同的参数来改变渲染效果,如attrs={'class':'form-control'} 就会给相应标签添加属性。 9.表单渲染添加css class可以通过widget。而<\label> 和 错误 通过定义form类是添加类属性 error_css_class 和 required_css_class 明天实验这两个hook钩子???? 10.其实还是不要用完整的,就用他们的label值和错误值,只用field的渲染就好了。 11.ValidationError导入使用from django.coreexceptions import ValidationError 12.内置widget都在forms模块中。
# form 的实例,可以是空,也可以提前填充数据。归纳总结form实例化数据主要来自三个方面: 1.来自model instance 2.来自其它数据源 3.来自用户提交的表单数据。这种情况通常给用户一个空form后,用户提交,如果无效,再返回一个绑定了数据的form给用户。
forms组件主要配合models里面的默写类一起使用 但是默写类里面的字段需要在forms类中相当于重写一遍 代码冗余 为了更好的结合forms与models的关系 有了一个ModelForm(基于forms组件)
class MyUser(forms.ModelForm): class Meta: model = models.User # 指定关联的表 fields = '__all__' # 所有的字段全部生成对应的forms字段 labels = { 'name': '用户名', 'age': '年龄', 'addr': '地址', 'email': '邮箱' } widgets = { "name": forms.widgets.TextInput(attrs={"class": "form-control"}), } def reg(request): form_obj = MyUser() if request.method == 'POST': form_obj = MyUser(request.POST) if form_obj.is_valid(): # form_obj.save() # 新增数据 edit_obj = models.User.objects.filter(pk=5).first() form_obj = MyUser(request.POST, instance=edit_obj) # 是新增还是保存就取决于有没有instance参数 form_obj.save() # 编辑数据 return render(request, 'reg.html', locals())
''' HTTP协议是无状态协议,也就是说每个请求都是独立的!无法记录前一次请求的状态。但HTTP协议中可以使用Cookie来完成会话跟踪!在Web开发中,使用session来完成会话跟踪,session底层依赖Cookie技术。 '''
Cookie翻译成中文是小甜点,小饼干的意思。在HTTP中它表示服务器送给客户端浏览器的小甜点。其实Cookie是key-value结构,类似于一个python中的字典。 随着服务器端的响应发送给客户端浏览器。然后客户端浏览器会把Cookie保存起来,当下一次再访问服务器时把Cookie再发送给服务器,以便服务器提取有用信息。
Cookie是由服务器创建,然后通过响应发送给客户端的一个键值对。客户端会保存Cookie,并会标注出Cookie的来源(哪个服务器的Cookie)。当客户端向服务器发出请求时会把所有这个服务器Cookie包含在请求中发送给服务器,这样服务器就可以识别客户端了!
- Cookie大小上限为4KB; - 一个服务器最多在客户端浏览器上保存20个Cookie; - 一个浏览器最多保存300个Cookie; 上面的数据只是HTTP的Cookie规范,但在浏览器大战的今天,一些浏览器为了打败对手,为了展现自己的能力起见,可能对Cookie规范“扩展”了一些,例如每个Cookie的大小为8KB,最多可保存500个Cookie等!但也不会出现把你硬盘占满的可能! 注意,不同浏览器之间是不共享Cookie的。也就是说在你使用IE访问服务器时,服务器会把Cookie发给IE,然后由IE保存起来,当你在使用FireFox访问服务器时,不可能把IE保存的Cookie发送给服务器。
# Cookie是通过HTTP请求和响应头在客户端和服务器端传递的: 'Cookie':请求头,客户端发送给服务器端; '格式':Cookie: a=A; b=B; c=C。即多个Cookie用分号离开; Set-Cookie:响应头,服务器端发送给客户端; 一个Cookie对象一个Set-Cookie: Set-Cookie: a=A Set-Cookie: b=B Set-Cookie: c=C
如果服务器端发送重复的Cookie那么会覆盖原有的Cookie,例如客户端的第一个请求服务器端发送的Cookie是:Set-Cookie: a=A;第二请求服务器端发送的是:Set-Cookie: a=AA,那么客户端只留下一个Cookie,即:a=AA。
rep = HttpResponse(...) 或 rep = render(request, ...) 或 rep = redirect() rep.set_cookie(key,value,...) rep.set_signed_cookie(key,value,salt='加密盐',...)
return HttpResponse()、return render()、return redirect()这三种响应体最终都是返回的HttpResponse,因此都可以设置cookie。详见后面源码解析和参数设置。
request.COOKIES['key'] request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)
request.COOKIES实际是一个字典,封装了这次请求过来的所有cookie键值
# 参数: - default: 默认值 - salt: 加密盐 - max_age: 后台控制过期时间
响应体.delete_cookie("键值")
response.delete_cookie("cookie_key",path="/",domain=name)
一般清除浏览器cookie的方法:ctrl+shift+delete
# 视图函数返回值 return HttpResonse() return render() return redirect() ... 不直接返回对象 而是先用变量名指代 然后操作对象方法 res = HttpResonse() return res res = render() return res res = redirect() return res # 基本使用 res.set_cookie() # 设置 res.COOKIE.get() # 获取 # 有很多视图函数需要添加登录认证 有时候又需要取消登录认证