博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
django~2
阅读量:7164 次
发布时间:2019-06-29

本文共 34375 字,大约阅读时间需要 114 分钟。

本节内容:

1:cookie和session

2:请求生命周期的FBV和CBV

3:model的操作,复习

4:分页的实现

5:Django Form

6:页面请求models里面的all时候的几种实现

7:文件上传

 

 

 

一:cookie和session

1:COOKIE诞生的背景和工作原理

cookie不属于http协议范围,由于http协议无法保持状态,但实际情况,我们却又需要“保持状态”,因此cookie就是在这样一个场景下诞生。cookie的工作原理是:由服务器产生内容,浏览器收到请求后保存在本地磁盘上的;当浏览器再次访问时,浏览器会自动带上cookie,这样服务器就能通过cookie的内容来判断这个是“谁”了。

2:session诞生的背景

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

3:cookie和session的结合

问题来了,基于http协议的无状态特征,服务器根本就不知道访问者是“谁”。那么上述的cookie就起到桥接的作用。(1)我们可以给每个客户端的cookie分配一个唯一的id,这样用户在访问时,通过cookie,服务器就知道来的人是“谁”。然后我们再根据不同的cookie的id,在服务器上保存一段时间的私密资料,如“账号密码”等等。(2)总结而言:cookie弥补了http无状态的不足,让服务器知道来的人是“谁”;但是cookie以文本的形式保存在本地,自身安全性较差;所以我们就通过cookie识别不同的用户,对应的在session里保存私密的信息以及超过4096字节的文本。(3)另外,上述所说的cookie和session其实是共通性的东西,不限于语言和框架

 4:设置cookie

def login(request):    print("COOKIE",request.COOKIES)    ##COOKIE {
'csrftoken': 'R3Z7KuPa1pUW63PWPIog9CNGzMOE427NQRFDXNPA2tEq9O5VKFenQ5ixcvELu6e6', 'sessionid': 'naqh3zfp2pd6zl8dh2hvuzv5wihhjbwh'} if request.method == "POST" and request.POST.get("username") == "yuan": ret = redirect("/index/") ret.set_cookie("username","yuan") ###设置cookie 组成一组键值对 return ret return render(request,"login.html")

5:cookie和session的配合

"COOKIE":{"sessionid":"就是session给你的随机字符串,用于打开session的","csrftoken":"中间件防止跨站伪造请求"}"SESSION":{"COOKIES中的随机字符串":{
"服务端的私密的数据"},} 注意:session可以保存在任何的位置,文件、数据库、缓存都是可以的。为什么我们再建表的时候会产生那么多表? 其中一个表就是session表。

实现cookie和session的配合:

from django.shortcuts import render,redirect,HttpResponse# Create your views here.def login(request):    print("COOKIE",request.COOKIES)    print("session",request.session) ##会发现没有提示,到setting的MIDDLEWARE的改成MIDDLEWARE_CLASSES    ##COOKIE {
'csrftoken': 'R3Z7KuPa1pUW63PWPIog9CNGzMOE427NQRFDXNPA2tEq9O5VKFenQ5ixcvELu6e6', 'sessionid': 'naqh3zfp2pd6zl8dh2hvuzv5wihhjbwh'} if request.method == "POST" and request.POST.get("username") == "yuan": # ret = redirect("/index/") # ret.set_cookie("username","yuan") ###设置cookie # return ret request.session["is_login"] = True request.session["username"] = "yuan" return redirect("/index/") return render(request,"login.html")def index(request): # username = request.COOKIES.get("username",None) # print(request.COOKIES) # ###{
'csrftoken': 'R3Z7KuPa1pUW63PWPIog9CNGzMOE427NQRFDXNPA2tEq9O5VKFenQ5ixcvELu6e6', 'sessionid': 'naqh3zfp2pd6zl8dh2hvuzv5wihhjbwh', 'username': 'yuan'} # if username == "yuan": # return HttpResponse("hello") if request.session.get("is_login") and request.session.get("username") == "yuan": return HttpResponse("hello") else: return redirect("/login/")
View Code

如果出现如下错误:

因为上文说了:session默认是保存到session的表中的,而你没有这个表就出错了:解决python manage.py migrate

D:\python\project\cookie_session>python manage.py makemigrationsNo changes detectedD:\python\project\cookie_session>python manage.py migrate

 

 下面我们再来最后的总结一下cookie和session的点:

一、操作Cookie

  获取cookie:request.COOKIES[key]

  设置cookie:response.set_cookie(key,value)

由于cookie保存在客户端的电脑上,所以,jquery也可以操作cookie。

$.cookie("list_pager_num", 30,{ path: '/' });

二、操作Session(session默认在服务器端保存15天)

  获取session:request.session[key]

  设置session:reqeust.session[key] = value

  删除session:del request.session[key]    

(这个删除其实就是把数据库的session_data更新为一个其他的值了,并没有立即删除)

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

二、请求生命周期的FBV和CBV

1:Django的请求生命周期:

指的是当用户在浏览器输入了:http://127.0.0.1:8888/index.html 后都发生了什么事情
1:生成请求体,发送Http请求2:服务端接收 从上到下匹配url 3:匹配成功后,执行指定的views的函数或类    URL -->(对应) 函数  ===》这叫FBV      URL -->(对应) 类   ===》 这叫CBV             请求到达CBV的类的时候,不会马上执行get或post方法        他直接执行继承View的dispatch方法通过反射去执行,get或post方法。         执行完get或post之后,要把结果给dispatch,由dispatch交给用户。        4:业务处理    -根据个人需求自定    -操作数据库        -支持原生sql         -Django的 ORM(可能是最牛的ORM框架)    ===》上面都处理完了产生返回给用户的结果《===    -响应内容        -响应头        -响应体

怎么往响应头加点东西:

def  post(self,request):    ret =  HttpResponse("CBV.POST") ##响应内容就是"CBV.POST"    ret['h1'] = 'v1'   ##这就是往响应头加上了h1 = v1    ret.set_cookie('c1','v2') ##还可以往响应头设置cookie    return ret
View Code

2:CBV的实现:

urlpatterns = [    url(r'^admin/', admin.site.urls),    url(r'^CBV$', views.CBV.as_view()),  ##cbv后面的/ 不加django会默认给你加的    url(r'^FBV$', views.index),]
urls
from  django.views import Viewclass CBV(View):   ##要继承View ,是get请求就执行get函数    def get(self,request):        ##根据请求头中的request.method执行对应的函数        # return  HttpResponse("CBV.GET")        return render(request,"CBV.html")    def  post(self,request):        return HttpResponse("CBV.POST")
views
##CBV.html#####
template

CBV中的dispatch方法: 找到类 -- >dispatch反射执行函数--->函数执行完 返回结果 --->dispatch函数--->dispatch再返回给用户

from  django.views import Viewclass CBV(View):   ##要继承View ,是get请求就执行get函数    def dispatch(self, request, *args, **kwargs):        result =super(CBV,self).dispatch(request, *args, **kwargs)        return result   ##dispatch这样写好像是没毛用,我们什么功能都没给他加,不过起码知道了起码要往哪里加功能        def get(self,request):        ##根据请求头中的request.method执行对应的函数        # return  HttpResponse("CBV.GET")        return render(request,"CBV.html")    def  post(self,request):        return HttpResponse("CBV.POST")

 三:model的操作,复习

1对多的操作

正向查找:(外键在自己表中,通过自己的外键字段)    ##通过学生表找到“q学生”对应的班级        ##基于对象:        obj = models.Student.objects.filter(sname="q学生").first()        cname = obj.s2c.cname        print(cname) ##python1        ###基于"__"        obj2 = models.Student.objects.filter(sname="q学生").values("s2c__cname")        print(obj2) #
反向查找:(通过关联自己为外键的类名_set) ##通过学生表找到“q学生”对应的班级 基于"__" ret2 = models.Classes.objects.filter(student__sname="q学生").values("cname") print(ret2) ##
查找对应的“python1”班级的学生: 基于对象: ret = models.Classes.objects.get(cname="python1") ret_sult = ret.student_set.values("sname") print(ret_sult)
View Code

多对多操作 : 

正向查找:    ##查询python1对应的老师    #基于对象:        # obj = models.Classes.objects.filter(cname="python1")[0]        # ret = obj.c3s.all().values("tname")   ##obj.c3s.all() 获取到所有的tid        # print(ret)  ##
#基于"__" # obj = models.Classes.objects.filter(cname="python1").values("c3s__tname") # print(obj) ##
反向查找:##查询alex教的班级 #基于对象: # obj = models.Teachers.objects.filter(tname="alex").first() # cc = obj.classes_set.all().values("cname") #obj.classes_set.all() 拿到的就是所有alex的班级 # print(cc) ##
#基于"__": obj = models.Teachers.objects.filter(tname="alex").values("classes__cname") print(obj) #
View Code

四.分页的实现

1:分页的原理:

其实际就是不让一次显示全部的数据,而是用切片的方式进行显现数据:

###一共有1-998条的数据USER_LIST = []for i in range(1,999):    temp = "root"+str(i)+"-"+str(i)    USER_LIST.append(temp)    def index(request):    per_page= 10    current_page = int(request.GET.get("p"))    ##p=1  显示数据:1-10  对应列表 0-10    ##p=2  显示数据:11-20  对应列表 10-20    ##应用小学学过的知识,简单的计算指定页码要显示的数据    ##start = (current_pae -1) * 10    ##end = current_page * 10        start = (current_page -1) * 10    end = current_page * 10    data = USER_LIST[start:end]    return render(request,"index.html",{
"user_list":data})

2:Django的内置分页

Django的内置分页,就是给我们提供了2个类,2个对象。 paginator 、page

from django.shortcuts import renderfrom django.core.paginator import Paginator, EmptyPage, PageNotAnIntegerL = []for i in range(999):    L.append(i)def index(request):    current_page = request.GET.get('p')    paginator = Paginator(L, 10)    # per_page: 每页显示条目数量    # count:    数据总个数    # num_pages:总页数    # page_range:总页数的索引范围,如: (1,10),(1,200)    # page:     page对象    try:        posts = paginator.page(current_page)        # has_next              是否有下一页        # next_page_number      下一页页码        # has_previous          是否有上一页        # previous_page_number  上一页页码        # object_list           分页之后的数据列表        # number                当前页        # paginator             paginator对象    except PageNotAnInteger:  ###p不是一个有效的页码默认显示第一页        posts = paginator.page(1)    except EmptyPage:        posts = paginator.page(paginator.num_pages)  ##一共100页,你请求200页的内容,给你返回最后一页的内容    return render(request, 'index.html', {
'posts': posts})

html

    
    {
    % for item in posts %}
  • {
    { item }}
  • {
    % endfor %}
Html
View Code

3:Django的扩展内置分页

 

4:自定义分页

分页组件:

class Pagerator(object):    def __init__(self,total_count,current_page,per_page_num=7,per_page_data=10):        self.total_count = total_count        try:            v = int(current_page)        except Exception as  e:            v = 1        if v<=1:            v = 1        self.current_page = v        self.per_page_data = per_page_data        self.per_page_num = per_page_num    @property    def start(self):        return (self.current_page - 1 )* self.per_page_data    @property    def end(self):        return self.current_page * self.per_page_data    @property    def max_page_num(self):        a,b = divmod(self.total_count,self.per_page_data)        if b:            a= a+1        return a    def page_range(self):        temp_list = []        part_page_num = round(self.per_page_num / 2)        ##首页        frist = "
  • 首页
  • " temp_list.append(frist) ##上一页 if self.current_page == 1: pre_page = "
  • 上一页
  • " else: pre_page = "
  • 上一页
  • "%(self.current_page-1) temp_list.append(pre_page) ##如果当前页面小于4的时候,默认就是1 - 最大页码 if self.current_page <= part_page_num: page_start = 1 page_end = self.per_page_num else: page_start = self.current_page - 3 page_end = self.current_page +3 ##当end_page加+3之后比最大的页面还大的时候,这就是bug了,加上if判断 if page_end >= self.max_page_num: page_end = self.max_page_num for i in range(page_start,page_end+1): if self.current_page == i : temp = "
  • %s
  • "%(i,i) else: temp = "
  • %s
  • " % (i, i) temp_list.append(temp) ##下一页 if self.current_page == self.max_page_num: nex_page = "
  • 下一页
  • " else: nex_page = "
  • 下一页
  • "%(self.max_page_num) temp_list.append(nex_page) ##尾页 last = "
  • 首页
  • "%(self.max_page_num) temp_list.append(last) return "".join(temp_list)
    Page.py

    view:

    USER_LIST = []for i in range(1,666):    temp = "root"+str(i)+"-"+str(i)    USER_LIST.append(temp)def index(request):    from  app01 import  Page    current_page = request.GET.get("p")    page_obj=Page.Pagerator(666,current_page)  ##传入当前页和总数    data = USER_LIST[page_obj.start:page_obj.end] ##    per_page_num = page_obj.page_range()  ##获取每页显示的页码    return render(request,"index.html",{
    "user_list":data,"per_page_num":per_page_num})
    View Code
    {
    % for user in user_list %}
  • {
    { user }}
  • {
    % endfor %}

      {
      { per_page_num|safe }}
    template

    效果图:

     五、Form

    Django的Form主要具有一下几大功能:

    • 生成HTML标签
    • 验证用户数据(显示错误信息)
    • HTML Form提交保留上次提交数据
    • 初始化页面显示内容

    1、创建Form类

    from django.forms import Formfrom django.forms import widgetsfrom django.forms import fields class MyForm(Form):    user = fields.CharField(        widget=widgets.TextInput(attrs={
    'id': 'i1', 'class': 'c1'}) ##给他设置属性 ) gender = fields.ChoiceField( choices=((1, '男'), (2, '女'),), initial=2, widget=widgets.RadioSelect ) city = fields.CharField( initial=2, widget=widgets.Select(choices=((1,'上海'),(2,'北京'),)) ) pwd = fields.CharField( widget=widgets.PasswordInput(attrs={
    'class': 'c1'}, render_value=True) )
    View Code

    2、View函数处理

    from django.shortcuts import render, redirectfrom .forms import MyForm  def index(request):    if request.method == "GET":        obj = MyForm()        return render(request, 'index.html', {
    'form': obj}) elif request.method == "POST": obj = MyForm(request.POST, request.FILES) if obj.is_valid(): ##进行验证 values = obj.clean() ##获取验证后的数据 print(values) else: errors = obj.errors ##获取验证失败的数据 里面的数据是一个个key:[错误列表] print(errors) return render(request, 'index.html', {
    'form': obj}) else: return redirect('http://www.google.com')
    View Code

    3、生成HTML

    {

    { form.user }} {
    { form.user.errors }}

    {

    { form.gender }} {
    { form.gender.errors }}

    {

    { form.city }} {
    { form.city.errors }}

    {

    { form.pwd }} {
    { form.pwd.errors }}

    View Code

    1、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-01TimeField(BaseTemporalField) 格式:11:12DateTimeField(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) ##文件单独放在了request.FILES 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) ##有 0 和 1 两个值 ... NullBooleanField(BooleanField) ##有 0 、1、null 三个值 ... 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) ##ChoiceField中比如select的value是字符串,可以将coerce = lambda x :int(x) 变成int类型 coerce = lambda val: val 对选中的值进行一次转换 empty_value= '' 空值的默认值 MultipleChoiceField(ChoiceField) ###多选select ... 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类型(了解) ...
    View Code

     注:UUID是根据MAC以及当前时间等创建的不重复的随机字符串

    >>> import uuid    # make a UUID based on the host ID and current time    >>> uuid.uuid1()    # doctest: +SKIP    UUID('a8098c1a-f86e-11da-bd1a-00112444be1e')    # make a UUID using an MD5 hash of a namespace UUID and a name    >>> uuid.uuid3(uuid.NAMESPACE_DNS, 'python.org')    UUID('6fa459ea-ee8a-3ca4-894e-db77e160355e')    # make a random UUID    >>> uuid.uuid4()    # doctest: +SKIP    UUID('16fd2706-8baf-433b-82eb-8c7fada847da')    # make a UUID using a SHA-1 hash of a namespace UUID and a name    >>> uuid.uuid5(uuid.NAMESPACE_DNS, 'python.org')    UUID('886313e1-3b8a-5372-9b90-0c9aee199e5d')    # make a UUID from a string of hex digits (braces and hyphens ignored)    >>> x = uuid.UUID('{00010203-0405-0607-0809-0a0b0c0d0e0f}')    # convert a UUID to a string of hex digits in standard form    >>> str(x)    '00010203-0405-0607-0809-0a0b0c0d0e0f'    # get the raw 16 bytes of the UUID    >>> x.bytes    b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f'    # make a UUID from a 16-byte string    >>> uuid.UUID(bytes=x.bytes)    UUID('00010203-0405-0607-0809-0a0b0c0d0e0f')
    View Code

    2、Django内置插件:

    我们说Form为什么我们写个:class myForm(forms.Form):    username = fields.CharField(max_length=17)    就可以在前端{
    { obj.username }} 就能够显示出。input框出来,obj是myForm的对象,而username 是属于CharField字段的,那么它的默认内置插件就是一个###CharField的继承说明##CharField继承Field。Field的默认插件就是TextInput。TextInput继承Input。Input返回input的字符class CharField(Field): ==> class Field(object): ==> class TextInput(Input): ==> widget = TextInput input_type = 'text' class Input(Widget): return format_html('
    ', flatatt(final_attrs))
    TextInput(Input)NumberInput(TextInput)EmailInput(TextInput)URLInput(TextInput)PasswordInput(TextInput)HiddenInput(TextInput)Textarea(Widget)DateInput(DateTimeBaseInput)DateTimeInput(DateTimeBaseInput)TimeInput(DateTimeBaseInput)CheckboxInputSelectNullBooleanSelectSelectMultipleRadioSelectCheckboxSelectMultipleFileInputClearableFileInputMultipleHiddenInputSplitDateTimeWidgetSplitHiddenDateTimeWidgetSelectDateWidget
    内置插件

    3:常用选择插件(上面说的那么多插件,了解就好)这个要记住了

    CharField的就是返回,是一个字符串的就可以,而要形成什么input是由里面的widget去指定。# 单radio,值为字符串# user = fields.CharField(#     initial=2,#     widget=widgets.RadioSelect(choices=((1,'上海'),(2,'北京'),)) ##只有select比较特殊,里面还有choices,去指定值。# )### 单radio,返回值为数字### user = fields.IntegerField(###     initial=2,###     widget=widgets.RadioSelect(choices=((1,'上海'),(2,'北京'),)) ##只有select比较特殊,里面还有choices,去指定值。### ) # 单radio,值为字符串 # user = fields.ChoiceField(#     choices=((1, '上海'), (2, '北京'),),#     initial=2,#     widget=widgets.RadioSelect# ) # 单select,值为字符串# user = fields.CharField(#     initial=2,#     widget=widgets.Select(choices=((1,'上海'),(2,'北京'),))# ) # 单select,值为字符串# user = fields.ChoiceField(#     choices=((1, '上海'), (2, '北京'),),#     initial=2,#     widget=widgets.Select# ) # 多选select,值为列表# user = fields.MultipleChoiceField(#     choices=((1,'上海'),(2,'北京'),),#     initial=[1,],#     widget=widgets.SelectMultiple# )  # 单checkbox# user = fields.CharField(#     widget=widgets.CheckboxInput()# )  # 多选checkbox,值为列表# user = fields.MultipleChoiceField(#     initial=[2, ],#     choices=((1, '上海'), (2, '北京'),),#     widget=widgets.CheckboxSelectMultiple# )

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

    方式一:

    from django.forms import Formfrom django.forms import widgetsfrom django.forms import fieldsfrom django.core.validators import RegexValidator 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().value_list('id','caption')                ##super必须放在第一个位置,因为它会执行父类的init方法,拷贝所有的静态字段,复制给self.fieldsdef    form1(request):    if request.method = "GET":        obj= MyForm()    ##每次创建一个obj对象的时候,就去执行MyForm中的init方法。这样就能做到了实时更新    else:        obj = MyForm(request.POST)        obj.is_valid():    return render(request,"form1.html",locals())
    View Code

    方式二:(不推荐,它要是想显示中文依赖model中的__str__)

    使用django提供的ModelChoiceField和ModelMultipleChoiceField字段来实现

    from django import formsfrom django.forms import fieldsfrom django.forms import widgetsfrom django.forms import models as form_modelfrom django.core.exceptions import ValidationErrorfrom django.core.validators import RegexValidator class FInfo(forms.Form):    authors = form_model.ModelMultipleChoiceField(queryset=models.NNewType.objects.all())    # authors = form_model.ModelChoiceField(                                            queryset=models.NNewType.objects.all(),                                            to_field_name='id')        ##默认select里面values是id,而我们要改变也是可以的里面的value      ##默认不能显示中文,依赖model
    View Code

     4:ajax实现Form

    Form :ajax提交    1:不能在后台进行 redirect         1.1:要想实现:obj.is_valid()的时候,给他json.dumps(一个status的字典)        1.2:前端success:function(arg){            判断(status="true"){                windows.localtion.href = 'http://www.baidu.com'}            }    2:错误信息需要自己显示
    from django.shortcuts import renderfrom django.shortcuts import redirect,HttpResponse# Create your views here.from  django import  formsfrom django.forms import  fieldsfrom django.forms import widgetsclass UserInfo(forms.Form):    username = fields.CharField(min_length=6,)    email = fields.EmailField(error_messages={
    "invalid":"格式不正确"}) city = fields.IntegerField( widget=widgets.Select( choices=[(1,'北京'),(2,'武汉'),(3,'天津')], ), )def ajax_form(request): if request.method == "GET": obj = UserInfo() return render(request,"ajax_form.html",locals()) else: import json ret = {
    "status":False,"message":None} obj = UserInfo(request.POST) if obj.is_valid(): print(obj.cleaned_data) # return redirect("http://www.baidu.com") ret["status"] = True return HttpResponse(json.dumps(ret)) else: print(type(obj.errors)) ##
    继承dict 默认能json只有基本数据类型, ##而ErrorDict 继承dict 所以可以被json.dumps ##ErrorDict就是帮你把数据变成了as_ul,as_data,as_text默认是as_ul ret["message"] = obj.errors from django.forms.utils import ErrorDict print(obj.errors) print(obj.errors.as_ul()) print(obj.errors.as_data()) print(obj.errors.as_text()) return HttpResponse(json.dumps(ret))
    views
    from django.conf.urls import urlfrom django.contrib import adminfrom app01 import viewsurlpatterns = [    url(r'^admin/', admin.site.urls),    # url(r'^my_form/', views.my_form),    url(r'^ajax_form.html/', views.ajax_form),]
    urls
    form action="/" method="POST" enctype="multipart/form-data">    

    {

    { form.user }} {
    { form.user.errors }}

    {

    { form.gender }} {
    { form.gender.errors }}

    {

    { form.city }} {
    { form.city.errors }}

    {

    { form.pwd }} {
    { form.pwd.errors }}

    ###第二种都显示所有的数据
    {
    {form.as_table}}
    ###第三种都显示所有的数据{
    {form.as_p}}##第四种都显示所有的数据{
    {form.as_ul}}
    template

    5:自定义验证规则

    方式一:

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

    方式二:

    class UserInfo1(fforms.Form):    username = fields.CharField()    phone = fields.RegexField(r'^159[0-9]+$',error_messages={'invalid':"必须以159开始的手机"})

    方式三:

    要是username是unique的,注册的时候不能重复,我们怎么通过Form进行判断说,“用户已经存在”

    前戏1:

    Form源码解析:

    当我们obj.is_valid()的时候,会有两个产物 cleand_data 、errors

    一:    def is_valid(self):        """        Returns True if the form has no errors. Otherwise, False. If errors are        being ignored, returns False.        """        return self.is_bound and not self.errors          ##会去执行self.errors的函数                二:    @property    def errors(self):        "Returns an ErrorDict for the data provided for the form"        if self._errors is None:            self.full_clean()   ###默认_errors 是None执行full_clean()方法        return self._errors        三:    def full_clean(self):        self._errors = ErrorDict()  ##创建一个ErrorDict的字典        if not self.is_bound:  # Stop further processing.            return        self.cleaned_data = {}   ##通过验证的字典        if self.empty_permitted and not self.has_changed():            return                    self._clean_fields()  ##执行_clean_fields方法,对每个字典进行匹配规则        self._clean_form()        self._post_clean()                四:    def _clean_fields(self):        for name, field in self.fields.items():  ##依次遍历Form字段            # value_from_datadict() gets the data from the data dictionaries.            # Each widget type knows how to retrieve its own data, because some            # widgets split data over several HTML fields.            if field.disabled:                value = self.initial.get(name, field.initial)            else:                value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))            try:                if isinstance(field, FileField):                    initial = self.initial.get(name, field.initial)                    value = field.clean(value, initial)                else:                    value = field.clean(value)                self.cleaned_data[name] = value   ##验证成功临时放入cleaned_data中["username":"crik","pwd":123]                if hasattr(self, 'clean_%s' % name):  ##要是在Form中有clean_字段名,要执行,通过这个,才真正的放入cleaned_data字段中                    value = getattr(self, 'clean_%s' % name)()                    self.cleaned_data[name] = value            except ValidationError as e:   ##如果有ValidationError,就add_error (name=字段名字,而e是错误)                self.add_error(name, e)
    View Code

    前戏2:

    通过源码,我们可以知道了在Form class中我们可以定义一个以"clean_"前缀的字段方法。

    进行额外的判断:

    注意:方法的返回值必须是self.cleand_data[name]=value 因为在第一次只是临时放入到cleand_data字典正确数据,

    实现:

    from  django import forms as fformsfrom django.forms import fieldsfrom django.forms import  widgetsfrom django.core.validators import RegexValidatorfrom app01 import  modelsfrom django.core.exceptions import NON_FIELD_ERRORS,ValidationErrorclass UserInfo(fforms.Form):    username = fields.CharField()    phone = fields.CharField(        validators=[RegexValidator(r'^[0-9]+$', '请输入数字'), RegexValidator(r'^159[0-9]+$', '数字必须以159开头')],        )    def clean_username(self):        v = self.cleaned_data['username']        if models.Users.objects.filter(username=v).count(): ##存在username=发过来的username,就报错            raise ValidationError('用户名已存在')        return v

    views

    from app01.myform import *import jsondef exform(request):    if request.method == "GET":        obj = UserInfo()        return render(request,"exForm.html",{
    "obj":obj}) else: obj = UserInfo(request.POST) if obj.is_valid(): print(obj.cleaned_data) return HttpResponse("OK") else: print(obj.errors) ##
    • username
      • 用户名已存在
    • phone
      • 数字必须以159开头
    return HttpResponse(json.dumps(obj.errors))

     方式四:

    对于方式三的时候,我们可以对其单个字段进行一个扩展,但是在设计数据库的时候我们是有存在联合唯一的key的,往往需要两个或三个字段唯一才报错。

    前戏:源码解析

    一:     def is_valid(self):        """        Returns True if the form has no errors. Otherwise, False. If errors are        being ignored, returns False.        """        return self.is_bound and not self.errors  ##执行self.errors函数        二:    @property    def errors(self):        "Returns an ErrorDict for the data provided for the form"        if self._errors is None:            self.full_clean()  ##执行full_clean函数        return self._errors        三:    def full_clean(self):        """        Cleans all of self.data and populates self._errors and        self.cleaned_data.        """        self._errors = ErrorDict()        if not self.is_bound:  # Stop further processing.            return        self.cleaned_data = {}        # If the form is permitted to be empty, and none of the form data has        # changed from the initial data, short circuit any validation.        if self.empty_permitted and not self.has_changed():            return        self._clean_fields()  ##在方式三的时候,这个函数是逐一进行匹配单个字段的、并且存在一个clean_字段名的扩展        self._clean_form()    ##开始执行_clean_form()函数         self._post_clean()四:    def _clean_form(self):        try:            cleaned_data = self.clean()  ##执行self.clean()方法,当clean里面有ValidationError的时候,就会被捕捉到        except ValidationError as e:                self.add_error(None, e)        ##以None为key的错误        else:            if cleaned_data is not None:                self.cleaned_data = cleaned_data                          ##from django.core.exceptions import NON_FIELD_ERRORS, ValidationError        ##NON_FIELD_ERRORS = '__all__'  定义的是以None为key,而他进行了一个转换none==__all__                 五:    def clean(self):  ##这就是一个钩子、我们可以做任何的事情。但是要记得必须要return self.cleaned_data        """        Hook for doing any extra form-wide cleaning after Field.clean() has been        called on every field. Any ValidationError raised by this method will        not be associated with a particular field; it will have a special-case        association with the field named '__all__'.        """        return self.cleaned_data
    View Code

    myforms文件

    from  django import forms as fformsfrom django.forms import fieldsfrom django.forms import  widgetsfrom django.core.validators import RegexValidatorfrom app01 import  modelsfrom django.core.exceptions import NON_FIELD_ERRORS,ValidationErrorclass UserInfo(fforms.Form):    username = fields.CharField()    phone = fields.CharField(        validators=[RegexValidator(r'^[0-9]+$', '请输入数字'), RegexValidator(r'^159[0-9]+$', '数字必须以159开头')],        )    def clean_username(self):        v = self.cleaned_data['username']        if models.Users.objects.filter(username=v).count():            raise ValidationError('用户名已存在')        return v    def clean(self):        username = self.cleaned_data['username']  ##这里可以获取到cleand_data的数据,因为单个字段验证完成        phone = self.cleaned_data['phone']        if username == "root" and phone == '159159': ##这里就做一个简单的实例,正常是去数据库匹配数据            raise ValidationError("用户已经注册")        return self.cleaned_data   ##必须返回cleand_data

    views

    from django.shortcuts import renderfrom django.shortcuts import redirect,HttpResponsefrom app01.myform import *import jsondef exform(request):    if request.method == "GET":        obj = UserInfo()        return render(request,"exForm.html",{
    "obj":obj}) else: obj = UserInfo(request.POST) if obj.is_valid(): print(obj.cleaned_data) return HttpResponse("OK") else: print(obj.errors) ##
    • __all__
      • 用户已经注册
    return HttpResponse(json.dumps(obj.errors))

     六、页面请求models里面的all时候的几种实现

    方式1:一次撸到底,单一页面

    #####views####def v1_all(request):    user_list = models.Users.objects.all()    return render(request,"v1_all.html",{
    "user_list":user_list}) #####v1_all.html####

    用户列表

    {
    % for row in user_list %}
    {
    % endfor %}
    {
    { row.id }}
    {
    { row.username }}
    View Code

    方式2:两个url,一个加载页面,一个加载数据

    views:

    def v1_all(request):    return render(request,"v1_all.html")def get_data(request):    user_list = models.Users.objects.all()    return render(request,"get_data.html",{
    "user_list":user_list})
    View Code

    template:

    {% for row in user_list %}            {
    { row.id }} {
    { row.username }} {
    % endfor %}
    get_data.html

    用户列表

    v1_all.html

    方式3:序列化的方式

    所谓的序列化,就是把一个对象转成字符串,能够保存在文件的东西,这一过程就是序列化。
    import jsonfrom django.core import serializersdef v1_all(request):    return render(request, "v1_all.html")def get_data(request):    ret = {
    "status": False, "message": None} user_list = models.Users.objects.all() if user_list.exists(): ##user_list存在数据,这一过程不会执行sql语句,执行去看一下是否有数据 ret["status"] = True ret["message"] = serializers.serialize("json", user_list) ## return HttpResponse(json.dumps(ret))
    views

    django的返回数据有:

    QuerySet的对象类型;     ##不是基本类型,不能直接采用json.dumps而是要采用Django内置的,serializers进行序列化,然后再进行json.dumpsvalues:QuerySet 包裹着字典 ; values_list :QuerySet 包裹着元组;    ##对其上的两种,我们可以采用去掉外面的QuerySet外衣 直接 list[user_list],    ##把QuerySet外衣变成list的外衣;然后就可以进行json.dumps

    七、文件上传

    views

    def upload(request):    if request.method == "GET":        return render(request,"upload.html")    else:        # print(request.POST) #
    # print(request.FILES) #
    ]}> user = request.POST.get("user") ##user是字符串 file = request.FILES.get("img") ##file是一个对象,包含文件内容、大小、文件名称等 # print(file.name) ##个人简历模板.doc # print(file.size) ##25088 f = open(file.name,'wb') for line in file.chunks(): ##文件的内容,默认是生成一个迭代器,要一点一点的去取数据 f.write(line) f.close() return HttpResponse(".....OK")

    template

    {
    % csrf_token %}
    NB上传
    ##可以自定制上传按钮。

     

    转载于:https://www.cnblogs.com/hero799/p/8874364.html

    你可能感兴趣的文章