感谢支持
我们一直在努力

Django实战教程

我想写[Django实战教程]这系列的文章是因为我最近在开发一个数据库脚本发布系统,该系统只限于公司内部使用,它的功能非常简单明了:开发人员通过该系统提交SQL语句,DBA审核后执行,并将结果反馈给开发人员。

当然,开发这种小系统方法很多,我选择的是Django+Bootstrap,Web框架选择Django是因为我对Python语言比较熟,平时经常用它写一些自动化脚本;前端框架选择Bootstrap是因为我很喜欢它那种小清新的界面风格。

这一系列的实战文章会详细记录我开发这个系统的过程,包括设计、源码等,因为我也是第一次使用Django,所以难免会有纰漏和错误,请大家多多指教。

所谓“工欲善其事,必先利其器”,下面介绍如何安装Django+Bootstrap。

Django 安装

在安装Django之前,系统上必须已经安装了Python,至于如何安装Python,这里就不多讲了,有兴趣的可以参考下我之前写的《[Python]安装及环境配置》,我选择的Python版本是2.7.3,这里建议大家不要使用Python 3,因为Python 3做了比较大的改动,目前还不太稳定,最重要的是很多API都改了,可能不兼容旧版本。

参考: Python学习总结—安装与配置环境  http://www.linuxidc.com/Linux/2012-11/73912.htm

安装好Python之后,建议安装easy_install,这样直接使用命令easy_install django即可下载最新版本,非常方便。

当然也可以采用源码安装,首先到Django官网下载tar.gz文件,然后:

 

tar xzvf Django-1.5.2.tar.gz
cd Django-1.5.2
sudo python setup.py install

 

 

django-bootstrap-toolkit 安装

关于Bootstrap的界面风格大家可以去Bootstrap的首页去看下,我个人很喜欢这种小清新的风格,

Bootstrap 的详细介绍:请点这里
Bootstrap 的下载地址:请点这里

相关阅读:

BootStrap入门教程图文详解 http://www.linuxidc.com/Linux/2013-08/88764.htm

dyve大牛开发了开源软件包django-bootstrap-toolkit,大家可以去github下载,链接地址是https://github.com/dyve/django-bootstrap-toolkit

该软件包中最重要的是“bootstrap_toolkit”文件夹,它是一个Django的app,待会儿需要把它拷贝至我们的项目,还有一个demo_project,这是一个demo项目。

通过该软件包,我们可以把Django和Bootstrap无缝结合起来,非常方便。这里顺便吐槽下百度,当我在百度里搜django bootstrap时,死活搜不到,但是用google搜第一个就是它,所以和技术相关的东西大家还是用google吧。

 

创建Django项目

Django安装成功后,就可以使用django-admin.py创建项目:

# django-admin.py startproject dbrelease

dbrelease是我为这个项目起的名字。

 

项目创建成功后,我们还要创建应用程序:

# python manage.py startapp dbrelease_app

然后再settings.py的INSTALLED_APPS添加’dbrelease_app’应用程序

现在我们运行一下Web服务器:

# python manage.py runserver 192.168.1.16:8000

在浏览器里输入http://192.168.1.16:8000/就可以看到Django淡蓝色的欢迎界面,因为现在还是一个空项目,除了欢迎界面其它的什么都没有,接下来的全部工作就是丰富页面内容。

为了能在新创建的Django项目中能使用bootstrap,我们需要把文件夹bootstrap_toolkit拷贝到项目的主文件夹下,并在settings.py文件中的INSTALLED_APPS里添加’bootstrap_toolkit’应用程序。

Django 的详细介绍:请点这里
Django 的下载地址:请点这里

推荐阅读:

Ubuntu Server 12.04 安装Nginx+uWSGI+Django环境 http://www.linuxidc.com/Linux/2012-05/60639.htm

 

更多精彩内容请看下一页: http://www.linuxidc.com/Linux/2013-09/90277p2.htm

在正式开始coding之前,我觉得有必要探讨下Django的MTV模式,理论和实践相结合,才能更好的掌握一门技术。
首先说说Web服务器开发领域里著名的MVC模式,所谓MVC就是把Web应用分为模型(M),控制器(C)和视图(V)三层,他们之间以一种插件式的、松耦合的方式连接在一起,模型负责业务对象与数据库的映射(ORM),视图负责与用户的交互(页面),控制器接受用户的输入调用模型和视图完成用户的请求,其示意图如下所示:

Django的MTV模式本质上和MVC是一样的,也是为了各组件间保持松耦合关系,只是定义上有些许不同,Django的MTV分别是值:
M 代表模型(Model):负责业务对象和数据库的关系映射(ORM)。
T 代表模板 (Template):负责如何把页面展示给用户(html)。
V 代表视图(View):负责业务逻辑,并在适当时候调用Model和Template。
除了以上三层之外,还需要一个URL分发器,它的作用是将一个个URL的页面请求分发给不同的View处理,View再调用相应的Model和Template,MTV的响应模式如下所示:

1,Web服务器(中间件)收到一个http请求
2,Django在URLconf里查找对应的视图(View)函数来处理http请求
3,视图函数调用相应的数据模型来存取数据、调用相应的模板向用户展示页面
4,视图函数处理结束后返回一个http的响应给Web服务器
5,Web服务器将响应发送给客户端
 
这种设计模式关键的优势在于各种组件都是松耦合的。这样,每个由 Django驱动的Web应用都有着明确的目的,并且可独立更改而不影响到其它的部分。
比如,开发者更改一个应用程序中的 URL 而不用影响到这个程序底层的实现。设计师可以改变 HTML页面的样式而不用接触Python代码。
数据库管理员可以重新命名数据表并且只需更改模型,无需从一大堆文件中进行查找和替换。
 
落到实处,Django的MTV模式相对应的python文件如下:

前两篇讲述了Django的理论,从这篇开始,我们真正进入Django实战部分,今天先从用户认证开始。

当大家平时打开一个网站时,第一步一般做什么?我猜大部分是先登录吧,所以我们就从用户认证开始。

 

打开用户认证

Django本身已经提供了用户认证模块,所以我们要做的事很简单,就是在它的基础上添加一些定制化的东西。默认情况下,Django的用户认证模块是打开的,可以通过以下步骤确认用户模块是否打开(在settings.py文件里):

1、确保 MIDDLEWARE_CLASSES 中包含 ‘django.contrib.sessions.middleware.SessionMiddleware’。
2、确认 INSTALLED_APPS 中有 ‘django.contrib.sessions’
3、将 ‘django.contrib.auth’ 放在你的 INSTALLED_APPS 设置中,然后运行 manage.py syncdb以创建对应的数据库表。
4、确认 SessionMiddleware 后面的 MIDDLEWARE_CLASSES 设置中包含 ‘django.contrib.auth.middleware.AuthenticationMiddleware’。

 

数据库配置

用户认证系统必然离不开数据库,因为用户信息需要保存在数据库里,Django自带的用户认证系统也不例外。在使用它之前,必须配置数据库,Django支持大部分的主流数据库,这里我采用的是Oracle数据库,首先需要安装cx_Oracle模块,Django必须通过它才能访问Oracle数据库。至于如何安装cx_Oracle模块,这里就不讲了,具体查看官方文档。

接着在Oracle中创建一个用户,我们Django项目创建的所有表都建在该用户下,因此需要相应的权限:

SQL> create user dbrelease identified by *** default tablespace dbrelease;

SQL> grant resource,create session,unlimited tablespace to dbrelease;

然后在settings.py中的Databases属性里设置django连接oracle:

DATABASES = {
    ‘default’: {
        ‘ENGINE’: ‘django.db.backends.oracle’,
        ‘NAME’: ‘lxdbtest’,                   
        ‘USER’: ‘dbrelease’,
        ‘PASSWORD’: ‘***’,
        ‘HOST’: ‘192.168.1.16’,                     
        ‘PORT’: ‘1521’,                     
    }
}

注意:上面的NAME是指Oracle的instance_name

 

好了,下面验证是否可以成功连接Oracle:

进入Django项目的主目录,运行python manage.py shell进入交互命令行,然后输入:

>>> from django.db import connection
>>> cursor = connection.cursor()

如果没有报错,说明已经配置成功。

 

最后执行syncdb语句,从以下输出中我们可以发现,用户认证系统默认在数据库里创建了以下表:

# python manage.py syncdb
Creating tables …
Creating table auth_permission
Creating table auth_group_permissions
Creating table auth_group
Creating table auth_user_groups
Creating table auth_user_user_permissions
Creating table auth_user
Creating table django_content_type
Creating table django_session
Creating table django_site

上一篇中,我们已经打开了Django自带的用户认证模块,并配置了数据库连接,创建了相应的表,本篇我们将在Django自带的用户认证的基础上,实现自己个性化的用户登录和注销模块。

首先,我们自己定义一个用户登录表单(forms.py):

from django import forms
from django.contrib.auth.models import User
from bootstrap_toolkit.widgets import BootstrapDateInput, BootstrapTextInput, BootstrapUneditableInput

class LoginForm(forms.Form):
    username = forms.CharField(
        required=True,
        label=u”用户名”,
        error_messages={‘required’: ‘请输入用户名’},
        widget=forms.TextInput(
            attrs={
                ‘placeholder’:u”用户名”,
            }
        ),
    )   
    password = forms.CharField(
        required=True,
        label=u”密码”,
        error_messages={‘required’: u’请输入密码’},
        widget=forms.PasswordInput(
            attrs={
                ‘placeholder’:u”密码”,
            }
        ),
    ) 
    def clean(self):
        if not self.is_valid():
            raise forms.ValidationError(u”用户名和密码为必填项”)
        else:
            cleaned_data = super(LoginForm, self).clean()

我们定义的用户登录表单有两个域username和password,这两个域都为必填项。

 

接下来,我们定义用户登录视图(views.py),在该视图里实例化之前定义的用户登录表单

from django.shortcuts import render_to_response,render,get_object_or_404 
from django.http import HttpResponse, HttpResponseRedirect 
from django.contrib.auth.models import User 
from django.contrib import auth
from django.contrib import messages
from django.template.context import RequestContext

from django.forms.formsets import formset_factory
from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage

from bootstrap_toolkit.widgets import BootstrapUneditableInput
from django.contrib.auth.decorators import login_required

from .forms import LoginForm

def login(request):
    if request.method == ‘GET’:
        form = LoginForm()
        return render_to_response(‘login.html’, RequestContext(request, {‘form’: form,}))
    else:
        form = LoginForm(request.POST)
        if form.is_valid():
            username = request.POST.get(‘username’, ”)
            password = request.POST.get(‘password’, ”)
            user = auth.authenticate(username=username, password=password)
            if user is not None and user.is_active:
                auth.login(request, user)
                return render_to_response(‘index.html’, RequestContext(request))
            else:
                return render_to_response(‘login.html’, RequestContext(request, {‘form’: form,’password_is_wrong’:True}))
        else:
            return render_to_response(‘login.html’, RequestContext(request, {‘form’: form,}))

该视图实例化了之前定义的LoginForm,它的主要业务逻辑是:

1. 判断必填项用户名和密码是否为空,如果为空,提示”用户名和密码为必填项”的错误信息

2. 判断用户名和密码是否正确,如果错误,提示“用户名或密码错误”的错误信息

3. 登陆成功后,进入主页(index.html)

其中,登录页面的模板(login.html)定义如下:

<!DOCTYPE html>
{% load bootstrap_toolkit %}
{% load url from future %}
<html lang=”en”>
<head>
    <meta charset=”utf-8″>
    <title>数据库脚本发布系统</title>
    <meta name=”description” content=””>
    <meta name=”author” content=”朱显杰”>
    {% bootstrap_stylesheet_tag %}
    {% bootstrap_stylesheet_tag “responsive” %}
    <style type=”text/css”>
        body {
            padding-top: 60px;
        }
    </style>
    <!–[if lt IE 9]>
    <script src=”//html5shim.googlecode.com/svn/trunk/html5.js”></script>
    <![endif]–>
    <script src=”//ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js”></script>
    {% bootstrap_javascript_tag %}
    {% block extra_head %}{% endblock %}
</head>

<body>

    {% if password_is_wrong %}
        <div class=”alert alert-error”>
            <button type=”button” class=”close” data-dismiss=”alert”>×</button>
            <h4>错误!</h4>用户名或密码错误
        </div>
    {% endif %}   
    <div class=”well”>
        <h1>数据库脚本发布系统</h1>
        <p>&nbsp;</p>
        <form class=”form-horizontal” action=”” method=”post”>
            {% csrf_token %}
            {{ form|as_bootstrap:”horizontal” }}
            <p class=”form-actions”>
                <input type=”submit” value=”登录” class=”btn btn-primary”>
                <a href=”/contactme/”><input type=”button” value=”忘记密码” class=”btn btn-danger”></a>
                <a href=”/contactme/”><input type=”button” value=”新员工?” class=”btn btn-success”></a>
            </p>
        </form>
    </div>

</body>
</html>

最后还需要在urls.py里添加:

    (r’^accounts/login/$’,  ‘dbrelease_app.views.login’),

最终的效果如下:

 

1)当在浏览器里输入http://192.168.1.16:8000/accounts/login/,出现如下登陆界面:

2)当用户名或密码为空时,提示”用户名和密码为必填项”,如下所示:

3)当用户名或密码错误时,提示“用户名或密码错误”,如下所示:

4)如果用户名和密码都正确,进入主页(index.html)。

 

既然有login,当然要有logout,logout比较简单,直接调用Django自带用户认证系统的logout,然后返回登录界面,具体如下(views.py):

@login_required
def logout(request):
    auth.logout(request)
    return HttpResponseRedirect(“/accounts/login/”)

上面@login_required表示只有用户在登录的情况下才能调用该视图,否则将自动重定向至登录页面。

urls.py里添加:

(r’^accounts/logout/$’, ‘dbrelease_app.views.logout’),

上一篇我们实现了用户的登录和注销模块,本篇我们将实现用户自己修改密码。

同样地,我们首先在forms.py创建修改密码的表单如下:

class ChangepwdForm(forms.Form):
    oldpassword = forms.CharField(
        required=True,
        label=u”原密码”,
        error_messages={‘required’: u’请输入原密码’},
        widget=forms.PasswordInput(
            attrs={
                ‘placeholder’:u”原密码”,
            }
        ),
    )
    newpassword1 = forms.CharField(
        required=True,
        label=u”新密码”,
        error_messages={‘required’: u’请输入新密码’},
        widget=forms.PasswordInput(
            attrs={
                ‘placeholder’:u”新密码”,
            }
        ),
    )
    newpassword2 = forms.CharField(
        required=True,
        label=u”确认密码”,
        error_messages={‘required’: u’请再次输入新密码’},
        widget=forms.PasswordInput(
            attrs={
                ‘placeholder’:u”确认密码”,
            }
        ),
    )
    def clean(self):
        if not self.is_valid():
            raise forms.ValidationError(u”所有项都为必填项”)
        elif self.cleaned_data[‘newpassword1’] <> self.cleaned_data[‘newpassword2’]:
            raise forms.ValidationError(u”两次输入的新密码不一样”)
        else:
            cleaned_data = super(ChangepwdForm, self).clean()
        return cleaned_data

这个表单有三个选项,分别是:原密码、新密码、确认密码,所有项都为必填项,且两次输入的新密码必须一样。

接着我们在views.py创建修改密码的视图,在该视图中实例化上面定义的表单:

@login_required
def changepwd(request):
    if request.method == ‘GET’:
        form = ChangepwdForm()
        return render_to_response(‘changepwd.html’, RequestContext(request, {‘form’: form,}))
    else:
        form = ChangepwdForm(request.POST)
        if form.is_valid():
            username = request.user.username
            oldpassword = request.POST.get(‘oldpassword’, ”)
            user = auth.authenticate(username=username, password=oldpassword)
            if user is not None and user.is_active:
                newpassword = request.POST.get(‘newpassword1’, ”)
                user.set_password(newpassword)
                user.save()
                return render_to_response(‘index.html’, RequestContext(request,{‘changepwd_success’:True}))
            else:
                return render_to_response(‘changepwd.html’, RequestContext(request, {‘form’: form,’oldpassword_is_wrong’:True}))
        else:
            return render_to_response(‘changepwd.html’, RequestContext(request, {‘form’: form,}))

其中,修改密码的模板changepwd.html的定义如下:

<!DOCTYPE html>
{% load bootstrap_toolkit %}
{% load url from future %}
<html lang=”en”>
<head>
    <meta charset=”utf-8″>
    <title>数据库脚本发布系统</title>
    <meta name=”description” content=””>
    <meta name=”author” content=”朱显杰”>
    {% bootstrap_stylesheet_tag %}
    {% bootstrap_stylesheet_tag “responsive” %}
    <style type=”text/css”>
        body {
            padding-top: 60px;
        }
    </style>
    <!–[if lt IE 9]>
    <script src=”//html5shim.googlecode.com/svn/trunk/html5.js”></script>
    <![endif]–>
    <script src=”//ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js”></script>
    {% bootstrap_javascript_tag %}
    {% block extra_head %}{% endblock %}
</head>

<body>

    <h2>修改密码</h2>
   
    {% if oldpassword_is_wrong %}
        <div class=”alert alert-error”>
            <button type=”button” class=”close” data-dismiss=”alert”>×</button>
            <h4>错误!</h4>原密码不正确
        </div>
    {% endif %}
    <div class=”well”>
        <form class=”form-horizontal” action=”” method=”post”>
            {% csrf_token %}
            {{ form|as_bootstrap:”horizontal” }}
            <p class=”form-actions”>
                <input type=”submit” value=”确认修改” class=”btn btn-primary”>
            </p>
        </form>
    </div>

</body>
</html>

在urls.py添加如下:

(r’^accounts/changepwd/$’, ‘dbrelease_app.views.changepwd’),

最终效果如下:

 

1)修改密码的登录框如下所示:

2)上述三个域都为必填项,只要有一个为空,就提示“所有项为必填项”的错误信息:

3)如果两次输入的新密码不同,提示“两次输入的新密码不一样”的错误信息:

4)如果原密码错误,提示“原密码错误”的错误信息:

如果所有的信息都填写正确,则修改密码成功,返回主页。

 

至此,我们的用户认证模块已经实现完毕。有些细心的读者可能已经发现,我们没有实现用户注册,这是因为我的这个系统是个公司内部员工使用的系统,实名绑定,不允许注册。当然,如果你要实现注册,也是非常简单的,这里就不再累述。

前面我们实现了用户认证模块,在该模块里其实已经涉及到模块(ORM),但由于是Django自带的,大部分它都已经帮我们处理好,对于我们来说是透明的,本篇,我们将自己定义模型。

在Web开发中,我习惯从数据库层面开始,一方面是因为我本身是做DBA的,对数据库比较了解;另一部分也是因为相比于模板和视图来说,模型从设计之初基本已确定,不会有大的改动。

我们的模型models.py定义如下:

from django.db import models
from django.contrib.auth.models import User

class Manager(models.Model):
    id = models.AutoField(primary_key=True)
    username = models.CharField(max_length=30)
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)
    email = models.EmailField(max_length=70)
    def __unicode__(self):
        return self.last_name + self.first_name

class Dba(models.Model):
    id = models.AutoField(primary_key=True)
    username = models.CharField(max_length=30)
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)
    email = models.EmailField(max_length=70)
    def __unicode__(self):
        return self.last_name + self.first_name

class State(models.Model):
    id = models.AutoField(primary_key=True)
    statename = models.CharField(max_length=20)
    def __unicode__(self):
        return self.statename

class Database(models.Model):
    id = models.AutoField(primary_key=True)
    databasename = models.CharField(max_length=30)
    databaseip = models.CharField(max_length=30)
    def __unicode__(self):
        return self.databasename + ‘ – ‘ + self.databaseip

class Task(models.Model):
    id = models.AutoField(primary_key=True)
    creater = models.ForeignKey(User)
    manager = models.ForeignKey(Manager)
    dba = models.ForeignKey(Dba)
    state = models.ForeignKey(State)
    databases = models.ManyToManyField(Database)
    sql = models.CharField(max_length=2000,blank=True,null=True)
    desc = models.CharField(max_length=2000,blank=True, null=True)
    createdtime = models.DateTimeField()
    lastupdatedtime = models.DateTimeField(blank=True,null=True)
    dbacomment = models.CharField(max_length=2000,blank=True,null=True)
    attachment = models.FileField(upload_to=’tasks’,blank=True,null=True)
    def __unicode__(self):
        return str(self.id)

其中最核心的表是最后一张Task,在业务逻辑上代表开发人员提交给DBA的一个数据库更改请求,其它所有表都是围绕这个表创建的:Manager表是Task.manager的外键,Dba表是Task.dba的外键,State表是Task.state的外键,其中比较特殊的是Task.databases和Database表是多对多的关系。还有一点要注意的是,Task.creater是Django自带的用户表auth_user的外键。

 

以上models.py定义了ORM关系,但当前这些表只存在models.py中,在数据库中还不存在,我们需要通过以下步骤同步数据库:

首先,通过以下语句验证models.py定义模型的正确性:

python manage.py validate

如果上面没有错误,就可以执行如下语句同步数据库了(在同步之前,一定要先确认settings.py中DATABASES属性已正确配置,具体看第3篇):

python manage.py syncdb

如果你想知道syncdb究竟在数据库里做了什么,可以执行如下语句获得DDL:

python manage.py sqlall app_name

至此,模型已经创建成功,之后我们就可以在Django中自由的操纵数据库了。

在上一篇中,我们自己定义了数据库模型(models.py),并已同步至数据库中,那么如何插入、更新、删除表中数据呢?方法有很多,比如直接写SQL语句在数据库层面插入或调用模型的API,但这两种方法都需要写代码实现,可不可以有更直观页面操作呢?答案当然是肯定的,这就是Django自带的Admin模块,本篇主要讲述它。

首先,我们需要通过以下步骤开启Admin模块:

第一步:对settings.py做如下修改:

1)保证INSTALLED_APPS中包含’django.contrib.auth’,’django.contrib.contenttypes’和’django.contrib.sessions’(因为我们之前已经使用了Django自动的用户认证系统,因此这三个包应该就在开启的状态)

2)在INSTALLED_APPS中把’django.contrib.admin’和’django.contrib.admindocs’的注释去掉

3)确保MIDDLEWARE_CLASSES 包含’django.middleware.common.CommonMiddleware’ 、’django.contrib.sessions.middleware.SessionMiddleware’ 和’django.contrib.auth.middleware.AuthenticationMiddleware’

第二步:运行 python manage.py syncdb 。这一步将生成管理界面使用的额外数据库表

第三步:将admin访问配置在URLconf(记住,在urls.py中). 默认情况下,命令django-admin.py startproject生成的文件urls.py是将Django admin的路径注释掉的,你所要做的就是取消注释。如下所示:

# Uncomment the next two lines to enable the admin:
from django.contrib import admin
admin.autodiscover()

urlpatterns = patterns(”,
    # Examples:
    # url(r’^$’, ‘dbrelease.views.home’, name=’home’),
    # url(r’^dbrelease/’, include(‘dbrelease.foo.urls’)),

    # Uncomment the admin/doc line below to enable admin documentation:
    url(r’^admin/doc/’, include(‘django.contrib.admindocs.urls’)),

    # Uncomment the next line to enable the admin:
    url(r’^admin/’, include(admin.site.urls)),

)

第四步:创建admin.py文件,内容如下:

from django.contrib import admin
from .models import *

admin.site.register(Manager)
admin.site.register(Dba)
admin.site.register(State)
admin.site.register(Database)
admin.site.register(Task)

当这一切都配置完毕之后,在浏览器中输入http://192.168.1.16:8000/admin/,登陆成功后就可以使用Admin管理工具了,界面如下:

 

从以上页面我们发现,除了我们自己定义的模型(Dbrelease_app下的表)外,还有我们之前讲过的Django自带的用户认证模块(Auth)和(Sites)。

当用户登录成功后,首先看到的是他自己之前提交的任务列表,本篇将实现该页面。

视图(views.py)里定义如下:

from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage
@login_required
def tasklist(request):
    username=request.user.username
    if len(Dba.objects.filter(username=username)) == 0: #User is not DBA, only shows his/her own tasklist
        userid=User.objects.filter(username=username)
        lines = Task.objects.filter(creater=userid).order_by(“-id”)
    else:  #User is DBA, shows all tasklist
        lines = Task.objects.order_by(“-id”)
    paginator = Paginator(lines, 10)
    page = request.GET.get(‘page’)
    try:
        show_lines = paginator.page(page)
    except PageNotAnInteger:
        # If page is not an integer, deliver first page.
        show_lines = paginator.page(1)
    except EmptyPage:
        # If page is out of range (e.g. 9999), deliver last page of results.
        show_lines = paginator.page(paginator.num_pages)
    return render_to_response(‘tasklist.html’, RequestContext(request, {‘lines’: show_lines,}))

该视图的业务逻辑如下:

1)判断用户名,如果是普通用户,只显示他自己的任务列表;如果是DBA,则显示所有用户的任务列表。(从Task模型中获取数据)

2)调用Bootstrap的分页器Paginator,每页显示10行。

最终,该视图返回模板tasklist.html,其定义如下:

{% extends “base.html” %}

{% load bootstrap_toolkit %}

{% block content %}

    <h1>任务列表</h1>

    <table class=”table table-striped table-hover”>
        <tr>
            <td><strong>ID</strong></td>
            <td><strong>提交者</strong></td>
            <td><strong>创建时间</strong></td>
            <td><strong>最后更新时间</strong></td>
            <td><strong>状态</strong></td>
        </tr>
        {% for line in lines %}
            <tr>
                <td><a href=”{%%20url%20’dbrelease_app.views.taskdetail’%20%}?taskid={{%20line.id%20}}”>{{ line.id }}</a></td>
                <td><a href=”{%%20url%20’dbrelease_app.views.taskdetail’%20%}?taskid={{%20line.id%20}}”>{{ line.creater.last_name }}{{ line.creater.first_name }}</a></td>
                <td><a href=”{%%20url%20’dbrelease_app.views.taskdetail’%20%}?taskid={{%20line.id%20}}”>{{ line.createdtime|date:”Y-m-d H:i:s” }}</a></td>
                <td><a href=”{%%20url%20’dbrelease_app.views.taskdetail’%20%}?taskid={{%20line.id%20}}”>{{ line.lastupdatedtime|date:”Y-m-d H:i:s” }}</a></td>
                <td><a href=”{%%20url%20’dbrelease_app.views.taskdetail’%20%}?taskid={{%20line.id%20}}”>{{ line.state }}</a></td>
            </tr>
            </a>
        {% endfor %}
    </table>

    {% bootstrap_pagination lines url=”/tasklist?page=1″ align=”center” size=”large” %}

{% endblock %}

上面的模板定义了5列(分别是ID、提交者、创建时间、最后更新时间和状态),每行上都加一个超链接,指向该Task的详细信息(对应的视图taskdetail,我们会在下一章讲述该视图)。

 

最终效果如下:

本章通过实现一个用户提交任务请求的页面,讲述表单、视图、模型、模板间的交互。

首先,我们需要定义一个表单(forms.py)

class CreatetaskForm(forms.Form):
    creater = forms.CharField(
        label=u”创建者”,
        widget=BootstrapUneditableInput()
    )
    manager = forms.ModelChoiceField(
        queryset=Manager.objects.all(),
        required=True,
        label=u”项目负责人”,
        error_messages={‘required’: u’必选项’},
    ) 
    databases = forms.ModelMultipleChoiceField(
        queryset=Database.objects.order_by(‘id’),
        required=True,
        label=u”数据库”,
        error_messages={‘required’: u’至少选择一个’},
        widget=forms.CheckboxSelectMultiple,
    )   
    sql = forms.CharField(
        required=False,
        label=u”执行SQL”,
        widget=forms.Textarea(
            attrs={
                ‘placeholder’:”请在表名前加上schema,如hospital要写成p95169.hospital”,
                ‘rows’:5,
                ‘style’:”width:100%”,
            }
        ),
    )
    desc = forms.CharField(
        required=False,
        label=u”描述”,
        widget=forms.Textarea(
            attrs={
                ‘placeholder’:”如果不是执行SQL(如数据的导入导出等),一定要在描述里说清楚”,
                ‘rows’:5,
                ‘style’:”width:100%”,
            }
        ),
    )
    attachment = forms.FileField(
        required=False,
        label=u”附件”,
        help_text=u”如果SQL文本过长,超过2000个字符,请上传附件”
    )
    def clean(self):
        if not self.is_valid():
            raise forms.ValidationError(u”以下红色标记部分为必选项”)
        elif self.cleaned_data[‘sql’] == u” and self.cleaned_data[‘desc’] == u” :
            raise forms.ValidationError(u”如果执行SQL为空,描述为必填项”)
        else:
            cleaned_data = super(CreatetaskForm, self).clean()
        return cleaned_data

该表单包含一个多选框和附件上传,然后我们创建一个视图来实例化该表单:

import datetime
@login_required
def createtask(request):
    if request.method == ‘GET’:
        form = CreatetaskForm(initial={
        ‘creater’:request.user.last_name + request.user.first_name,
        })
        return render_to_response(‘createtask.html’, RequestContext(request, {‘form’: form,}))
    else:
        form = CreatetaskForm(request.POST,request.FILES)
        if form.is_valid():
            username = request.user.username
            t = Task.objects.create(
                creater = User.objects.get(username=username),
                manager = form.cleaned_data[‘manager’],
                dba = Dba.objects.get(id=1),
                state = State.objects.get(statename=’Open’),
                sql = form.cleaned_data[‘sql’],
                desc = form.cleaned_data[‘desc’],
                createdtime = datetime.datetime.now(),
                lastupdatedtime = datetime.datetime.now(),
                attachment = form.cleaned_data[‘attachment’],
            )
            databaselist = form.cleaned_data[‘databases’]
            for db in databaselist:
                t.databases.add(db)
            t.save()
            return render_to_response(‘base.html’, RequestContext(request,{‘createtask_success’:True,}))
        else:
            return render_to_response(‘createtask.html’, RequestContext(request, {‘form’: form,}))

该视图的业务逻辑是:

 

1)当用户提交表单后,验证表单的正确性

2)如果表单填写不正确,显示错误提示信息

3)如果表单填写正确,在数据库表Task里插入一条信息

其中,模型createtask.html的定义如下:

{% extends “base.html” %}

{% load bootstrap_toolkit %}

{% block extra_head %}
    {{ form.media }}
{% endblock %}

{% block content %}

    <h1>创建任务</h1>
   
    <div class=”well”>   
    <form enctype=”multipart/form-data” class=”form-horizontal” action=”” method=”post”>
        {% csrf_token %}
        {{ form|as_bootstrap:”horizontal” }}
        <p class=”form-actions”>
                <input type=”submit” value=”提交” class=”btn btn-primary”>
                <a href=”/tasklist/”><input type=”button” value=”返回” class=”btn”></a>
        </p>
    </form>
    </div>
{% endblock %}

最终的效果如下所示:

 


如果用户没有输入足够的信息,将提示错误信息如下:

如果用户所有的信息都填正确,则提交成功,提示成功信息:

点击OK,返回任务列表,排在第1的就是刚刚创建的任务:

赞(0) 打赏
转载请注明出处:服务器评测 » Django实战教程
分享到: 更多 (0)

听说打赏我的人,都进福布斯排行榜啦!

支付宝扫一扫打赏

微信扫一扫打赏