python2.0_day19_充分使用Django_form实现前端操作后台数据库

在前面的<python2.0_day19_学员管理系统之前端用户交互系统>一节中,我们实现了前端展示customer客户纪录.在<python2.0_day19_前端分页功能的实现>一节中,我们实现了网页中最常用的分页功能.最终我们在访问客户咨询纪录表的前端页面的效果如图:

能实现这个效果,对于我们这种新手,算是小有成就了.那么接下来,我们想实现点击前面的ID号,1,2,3就能进入该条目的编辑页面.这也是在Django admin后台管理中常见到的.那么我们之前在<python2.0_day18_课堂内容总结_DjangoForm表单>一节中,实现了通过Django的 Form类,实现展示一条纪录在前端,并且可以编辑保存.由此我们知道了,django的Form表单类可以实现两个功能:1.返回给前端页面的html代码部分 2.将纪录中的数据部分返回给前端.既然Django的Form类那么好用,我们在使用Django框架中想实现编辑某一个条目或者创建某一个条目当然要使用Django的Form表单类了.当然你也可以自己通过也前端代码和后台查询(能实现,但是又何必呢!)

那么我们接下来就来实现上述功能:1.在ID:1,2,3处做一个<a></a>标签.    那么a标签链接到哪里呢?    我们看上图的访问url地址:http://127.0.0.1:8000/crm/customers/    我们能不能如果想修改或者查看ID为1的条目是url地址为:http://127.0.0.1:8000/crm/customers/1/    这样就很好了,所以a标签应该链接的代码如下:        <td><a href="/crm/customers/{{coustomer.id}}/">{{ coustomer.id }}</a></td>    对没错,这也是我们新手所能够想到的,但对于Alex这种老道的大王,会预知一些坑,对于这些未来的坑,提前填补.我们来看看Alex的高明做法.

2. 修改crm/urls.py文件,添加一条/crm/customers/1/的URL路由条目
    from django.conf.urls import url
    from crm import views

    urlpatterns = [
        url(r‘^$‘, views.dashboard),
        url(r‘^customers/$‘, views.customers),
        url(r‘^customers/(\d+)/$‘, views.customer_detail),
    ]
3. 我们要使用Django的Form表单类,所以这要创建一个forms.py文件,并创建一个form类
    #!/usr/bin/env python3.5
    # -*- coding:utf-8 -*-
    # Author: Ming Zhou

    from django.forms import Form,ModelForm
    from crm import models
    # Form的好处时,他不在定义的form类中与models的表做关联,关联都是后台取数据后,自己按需求取得字段然后,插入到相应的表中
    # 优点: 所以Form设置很多字段,字段值传到后台了,可以和多个表进行关联
    # 缺点: 由于Form前面没有和表关联,导致外键(1对多,多对多)的标签无法实现自动关联.as

    # ModelForm 类创建时就要指定关联的表,
    # 优点: 很好的关联外键
    # 缺点: 一个modelform类只能关联一张表
    class CustomerModelForm(ModelForm):
        class Meta:
            model = models.Customer
            # filter = ()
            exclude = ()
ModelForm类创建好了.下面就可以在视图中引用了

    4.在crm/views.py文件中引用CustomerModelForm类,实例化对象后返回给前端
from django.shortcuts import render
    from crm import models
    from django.core.paginator import   Paginator,EmptyPage,PageNotAnInteger # 导入两个异常
    from crm import forms #引入forms
    # Create your views here.

    def dashboard(request):
        return render(request,‘crm/dashboard.html‘)
    def customers(request):
        customer_list = models.Customer.objects.all()
        paginator = Paginator(customer_list,3) # 每页显示2条纪录

        page = request.GET.get(‘page‘) #获取客户端请求传来的页码

        try:
            customer_list = paginator.page(page) # 返回用户请求的页码对象
        except PageNotAnInteger:   # 如果请求中的page不是数字,也就是为空的情况下
            customer_list = paginator.page(1)
        except EmptyPage:
            # 如果请求的页码数超出paginator.page_range(),则返回paginator页码对象的最后一页
            customer_list = paginator.page(paginator.num_pages)

        return render(request,‘crm/customers.html‘,{‘customer_list‘:customer_list})

    def customer_detail(request,customer_id): # 定义customer_detail.html网页的视图函数
        # 这个函数有两个功能:
        # 1.获取该记录的详细信息,用form表单形式展示
        # 2.修改该条纪录,并且保存.
        # 获取时用页面请求用的get方法,修改时请求用的post方法
        customer_obj = models.Customer.objects.get(id=customer_id)
        form = forms.CustomerModelForm()

        return render(request,‘crm/customer_detail.html‘,{‘customer_form‘:form}) #默认返回的时候,把form对象作为参数返回给前端页面
5.接着我们就要创建customer_detail.html文件了,内容如下:
    {% extends ‘base.html‘ %}
    {% load custom_tags %}
    {% block page-header %}
        {{Customers_detail}}
    {% endblock %}
    {% block page-content %}
        {{ customer_form }}  //这里我们就直接把后端传过来的{‘customer_form‘:form},作为参数写到代码里.
    {% endblock%}
6.访问测试http://127.0.0.1:8000/crm/customers/1/结果如图:

我们看到反问的结果,并不是我们想要的把,1条目里的内容都填入到表单内的标签里.这是因为我们在customer_detail视图函数里没有把获得到的customer_obj对象传入到实例化的form对象中.于是视图函数要改成如下:
    def customer_detail(request,customer_id):
        customer_obj = models.Customer.objects.get(id=customer_id)
        form = forms.CustomerModelForm(instance=customer_obj) # 这里是关键,把对象传入到form对象中用 instance属性

        return render(request,‘crm/customer_detail.html‘,{‘customer_form‘:form}) #默认返回的时候,把form对象作为参数返回给前端页面
7.再次访问测试http://127.0.0.1:8000/crm/customers/1/结果如图:

我们看到图是进来了,但是太丑了.我们看到Django admin的后台那个界面是好看,而这里相当丑.我们先把丑的问题解决掉.我们知道form类有两个功能,是返回给请求的不仅有数据,而且有html代码.既然customer_detail.html直接应用{{Customers_detail}}所以我们这里想改这个样式有两个思路:    1.for循环{{Customers_detail}} ,对每一个字段进行设置.这实现起来比较繁琐.    2. 在form类中直接指定某些字段对样式.我们这里用第2种:    首先我们要知道前端页面用了bootstrap,bootstrap本身就有美化表单的class样式,这里经常用的class是"form-control"    所以我们就把字段的class属性设置成form-control    代码如下:
        #!/usr/bin/env python3.5
        # -*- coding:utf-8 -*-
        # Author: Ming Zhou

        from django.forms import Form,ModelForm
        from crm import models
        class CustomerModelForm(ModelForm):
            class Meta:
                model = models.Customer
                # filter = ()
                exclude = ()

            # 先继承,再重写(想重新定义某些字段的属性,必须先继承,在重写,就这样记着)
            def __init__(self,*args,**kwargs):
                super(CustomerModelForm,self).__init__(*args,**kwargs)
                self.fields[‘qq‘].widget.attrs["class"] = "form-control"  # self.fields获得每一个字段的字典.这里是对qq字段进行了属性设置
8.我们再次访问测试:

是好看了,但是问题来了,如果有N多个字段,用这种在modelform类中定义字段表单属性的方式,要写很多.有没有简单的办法可以一次性把所有字段的样式加上(当然前提是每一个字段的样式都一样)Django这样强大,当然是可以实现的.for循环所有字段.
    #!/usr/bin/env python3.5
    # -*- coding:utf-8 -*-
    # Author: Ming Zhou

    from django.forms import Form,ModelForm
    from crm import models

    class CustomerModelForm(ModelForm):
        class Meta:
            model = models.Customer
            # filter = ()
            exclude = ()

        # 先继承,再重写(想重新定义某些字段的属性,必须先继承,在重写,就这样记着)
        def __init__(self,*args,**kwargs):
            super(CustomerModelForm,self).__init__(*args,**kwargs)
            # self.fields[‘qq‘].widget.attrs["class"] = "form-control"

            # for循环每一个字段,修改字段的class属性
            for field_name in self.base_fields:      #self.base_fields说白了就是把所有字段取出来
                field = self.base_fields[field_name]
                #这里可以是上面的例子field.widget.attrs["class"] = "form-control",也可以用update,用update的好处就是可以同时修改多个属性了
                field.widget.attrs.update({‘class‘:"form-control"})
这时候就给所有字段增加了class属性了.9.再次访问查看结果:

10.查看我们已经做到了,如何实现在此界面上更改后保存呢?我们查看modelform传到前端的代码部分,不是一个form表单,而是很多input标签.如果想保存,是不是应该在这些input标签上层加上form标签更改customer_detail.html
    {% extends ‘base.html‘ %}
    {% load custom_tags %}
    {% block page-header %}
        {{Customers_detail}}
    {% endblock %}
    {% block page-content %}
        <form action="" method="post">  #加上form标签
           {{ customer_form }}
        </form>

    {% endblock%}
写上这个后,是可以提交了.但是问题来了,我们看Django admin后台

我们的界面如何实现这两个功能呢?我们先把表单的样式搞定,首先后台做的事情已经OK了,要变的漂亮就要从前端动手了.前端页面的美化我们又再次想到bootstrap,我们在bootcss.com网站上找到关于表单的css全局样式.

11.把customer_detail.html代码改成如下:
    {% extends ‘base.html‘ %}
    {% load custom_tags %}
    {% block page-header %}
        {{Customers_detail}}
    {% endblock %}
    {% block page-content %}
        <form class="form-horizontal" method="post">
            {% for field in customer_form %}
                <div class="form-group">
                    <!--<label for="inputEmail3" class="col-sm-2 control-label">Email</label>-->
                    <!--for="inputEmail3" 为前端验证部分,这里用不到-->
                    <label class="col-sm-2 control-label">{{ field.label }}</label>
                    <!--field.label表示的就是字段头,说明modelform类里有label属性-->
                    <div class="col-sm-10">
                        <!--<input type="email" class="form-control" id="inputEmail3" placeholder="Email">-->
                        <!--这整个input就不需要了,因为我们后台返回的form对象就有html代码.这里我们直接把for循环的field放在这里即可-->
                        {{ field }}
                    </div>
                </div>
            {% endfor%}
            <input class="btn btn-success pull-right" type="submit" value="Save">  # 添加一个提交按钮

        </form>

    {% endblock%}
这里有两个知识点,一个是for field in modelform对象时,field.label获取字段名称.{{field}}直接显示input标签,不会重复显示字段名称.12.接着我们访问测试结果如下:

接下来,我们先不实现前面图中的必填项加粗的功能.先看下修改纪录的明细字段后,提交保存13.我们先随意修改下,会发现报错如下:

这个在之前的form中已经看到过,是由于csrf的问题解决就是在form中添加{% csrf_token %},具体为什么下节内容会讲到.form标签处更改如下:
<form class="form-horizontal" method="post">{% csrf_token %}
14.更改views.py文件添加判断post的处理方法,代码如下:
   def customer_detail(request,customer_id):
        customer_obj = models.Customer.objects.get(id=customer_id)
        if request.method == "POST":  #判断是不是POST,如果是POST,准备保存修改

            # 保存修改之前呢,先做验证,那么我们把request.POST传入CustomerModelForm类了,instance=customer_obj还需要吗?
            # 当然需要,为什么呢?因为如果没有instance=customer_obj这个属性,代表的是创建了.创建就有问题了,我们是修改.
            form = forms.CustomerModelForm(request.POST,instance=customer_obj)

            # 接下来对这个form进行验证
            if form.is_valid():
                form.save()

        #如果是GET,那么直接返回.
        else:
            form = forms.CustomerModelForm(instance=customer_obj)

        return render(request,‘crm/customer_detail.html‘,{‘customer_form‘:form}) #默认返回的时候,把form对象作为参数返回给前端页面
15.结下来我们打开浏览器,修改提交,然后在通过查看admin后台,看下具体数据是否被修改提交.

    

通过上图我们已经实现了,修改提交.但问题是,admin管理后台都是修改提交后返回的是纪录列表,而我们的是在此返回这条纪录的明细.那么我们在改下views.py让接收POST提交的数据库保存后,跳转到crm/customers.html.但是问题来了,我们这里写跳转,不应该写死,应该写成动态的,你看我们现在在urls.py定义纪录明细的URL是    url(r‘^customers/(\d+)/$‘, views.customer_detail),那么当他的上级改了,改成其它了,不叫customers/了,你视图里也要改.根据Alex经验判断,这里写成动态的最好.于是代码如下:
    from django.shortcuts import render,redirect 导入redirect
    def customer_detail(request,customer_id):
        # 这个函数有两个功能:
        # 1.获取该记录的详细信息,用form表单形式展示
        # 2.修改该条纪录,并且保存.
        # 获取时用页面请求用的get方法,修改时请求用的post方法
        customer_obj = models.Customer.objects.get(id=customer_id)
        if request.method == "POST":  #判断是不是POST,如果是POST,准备保存修改

            # 保存修改之前呢,先做验证,那么我们把request.POST传入CustomerModelForm类了,instance=customer_obj还需要吗?
            # 当然需要,为什么呢?因为如果没有instance=customer_obj这个属性,代表的是创建了.创建就有问题了,我们是修改.
            form = forms.CustomerModelForm(request.POST,instance=customer_obj)

            # 接下来对这个form进行验证
            if form.is_valid():
                form.save()
                print(‘url:‘,request.path)# 打印结果(‘url‘,u‘/crm/customers/1/‘),我们看和他的上级目录就多了/1/,所以我们把/1/去掉即可
                base_url = "/".join(request.path.split("/")[0:-2])  #这里 [0:-2]的意思取第一个直至倒数第三个,简写成 [:-2],为啥不是倒数第二个[:-1],因为request.path.split("/")的结果后面有一个空字符串‘‘
                #return redirect("/crm/customers/")
                print(base_url)
                return redirect(base_url)
        #如果是GET,那么直接返回.
        else:
            form = forms.CustomerModelForm(instance=customer_obj)

        return render(request,‘crm/customer_detail.html‘,{‘customer_form‘:form}) #默认返回的时候,把form对象作为参数返回给前端页面
16.最后我们来实现必填项加粗的前端,还有把验证未通过的错误信息输出.首先更改前端的显示,就不要修改后台views的函数了,所以实现这两个功能只在前端更改即可代码如下:
    {% extends ‘base.html‘ %}
    {% load custom_tags %}
    {% block page-header %}
        {{Customers_detail}}
    {% endblock %}
    {% block page-content %}
        <form class="form-horizontal" method="post">{% csrf_token %}
            {% for field in customer_form %}
                <div class="form-group">
                    <!--<label for="inputEmail3" class="col-sm-2 control-label">Email</label>-->
                    <!--for="inputEmail3" 为前端验证部分,这里用不到-->
                    {% if field.field.required %} //这里是判断字段的必填属性是不是为true
                        <label class="col-sm-2 control-label">{{ field.label }}</label>
                        <!--field.label表示的就是字段头,说明modelform类里有label属性-->
                    {% else %}
                        <label style="font-weight: normal" class="col-sm-2 control-label">{{ field.label }}</label>
                    {% endif %}
                    <div class="col-sm-10">
                        <!--<input type="email" class="form-control" id="inputEmail3" placeholder="Email">-->
                        <!--这整个input就不需要了,因为我们后台返回的form对象就有html代码.这里我们直接把for循环的field放在这里即可-->
                        {{ field }}
                        {% if field.errors %}   //如果错误不为空,当然错误不止一个,所以要循环显示
                            <ul>
                                {% for error in field.errors %}
                                <li style="color: red">{{error}}</li>
                                {% endfor %}
                            </ul>
                        {% endif %}
                    </div>
                </div>
            {% endfor%}
            <input class="btn btn-success pull-right" type="submit" value="Save">
        </form>

    {% endblock%}
17.最终访问页面测试结果如图:

至此我们实现了呵admin后台修改功能差不多的界面,当然还不够

				
时间: 2024-10-22 00:33:24

python2.0_day19_充分使用Django_form实现前端操作后台数据库的相关文章

前端和后台对时间数值的增减操作(JavaScript和C#两种方法)

最近在做一个视频回放项目,记录下一点总结. 应用背景: 假设有一个门禁系统记录着门禁的人员进出刷卡信息,门禁装有视频录像设备,现在要根据人员的刷卡时间调出其刷卡时间点前后一段时间的录像.关于视频回放部分具体实现过程较复杂本文不涉及,提一下其中涉及到的对时间数值的增减操作. 目标: 以时间格式的字符串“2015-01-01 00:00:00”为例,返回该时间点前30秒和后30秒的时间格式的字符串. 前端和后台起始都可以实现,只是方法不同而已. 后台(C#)实现方法: 1 string str =

移动前端开发之数据库操作篇

在移动平台开发中,经常会有大数据存储与交互的操作,在以webkit为内核的浏览器中,提供了一个叫作WEBSQL的数据库.这让我们前端也可以像php等程序语言一样,进行数据库的读写操作.Web Storage存储本地数据的方法目前可以在许多主流浏览器.平台与设备上实现,与之相关的API也已经标准化,但是,Web Storage存储空间是有5MB,键值存储的方式带来诸多不便,未来本地存储也不仅仅是这一种方法.其中最为熟知的就是Web SQL数据库,它内置SQLite数据库,对数据库的操作可以通过调用

Python学习之使用Python操作Redis数据库

最近在写一个检查一台服务器上所有游戏区服配置文件中redis某个key值大小的脚本,本打算使用shell+awk+sed的方式去解决这个问题,但是由于redis的配置信息是php数组形式.shell脚本一时没有写出来,就请教他人帮忙写了个python脚本,但是自己python不是很精通,于是按照脚本中涉及到的python知识现学现用,然后根据自己的需求更改脚本.这里分享一下如何使用python操作redis数据库. Redis的Python驱动源码下载地址是https://github.com/

EasyUI中datagrid实现显示、增加、 删除、 修改、 查询操作(后台代码C#)

2datagrid加载数据.代码如下所示 一.数据的显示 1新建HtmlPage2.html页面,引入相关文件.如下所示 <script src="easyui/js/jquery-1.8.2.min.js"></script>  <script src="easyui/js/jquery.easyui.min.js"></script>  <link href="easyui/css/themes/d

Struts2 前端与后台之间传值问题

老是被前端与后台之间的传值给弄糊涂了,特此写一篇blog进行总结. 一. 前端向后台传值 (1)属性驱动 属性驱动是指在Action类里,包含表单里对应的字段(字段名称一样),同时设置对应的getter和setter. 表单代码:html 1: <form action="login" method="post"> 2: <label>username:</label> 3: <input type="text&q

HTML5 indexedDB前端本地存储数据库实例教程 (转载)

一.indexedDB为何替代了Web SQL Database? 跟小朋友的教育从来没有什么“赢在起跑线”这种说法一样,在前端领域,也不是哪来先出来哪个就在日后引领风骚的. HTML5 indexedDB和Web SQL Database都是本地数据库数据存储,Web SQL Database数据库要出来的更早,然并卵.从2010年11月18日W3C宣布舍弃Web SQL database草案开始,就已经注定Web SQL Database数据库是明日黄花. 未来一定是indexedDB的,从

python操作mysql数据库(一)

最近又开始重新学习python,研究了一些python操作mysql数据库的知识,记录在此,用作学习笔记, 基础环境:Python 3.5.1 mysql版本:5.6.35 (rpm安装方式) 操作系统:Centos7.3 和windows7 一.python连接数据库模块介绍: 目前主要用的有以下几种.MySQLdb和pymsql以及mysql官方提供的mysql-connector-python驱动,MySQLdb模块是python2.X使用比较多的,而python3.X使用的pymsql会

通过AngularJS实现前端与后台的数据对接(二)——服务(service,$http)篇

基础知识 1.service(): 使用service()可以注册一个支持构造函数的服务,它允许我们为服务对象注册一个构造函数. service()方法接受两个参数:   ? name(字符串) 要注册的服务名称. ? constructor(函数) 构造函数,我们调用它来实例化服务对象. 2.$http(): 使用$http()服务可以将应用同来自远程服务器的信息集成在一起. $http服务是只能接受一个参数的函数,这个参数是一个对象,包含了用来生成HTTP请求的配置内容.这个函数返回一个pr

Linux下使用Python操作MySQL数据库

安装mysql-python 1.下载mysql-python 打开终端: cd /usr/local sudo wget http://nchc.dl.sourceforge.net/sourceforge/mysql-python/MySQL-python-1.2.2.tar.gz 官网地址:http://sourceforge.net/projects/mysql-python/ 2.解压 sudo tar -zxvf MySQL-python-1.2.2.tar.gz cd MySQL-