×

Django 实战1:搭建属于自己社工查询系统(上)

96
猴哥Yuri
2018.04.09 09:30* 字数 1560
题图:by click_vision from Instagram

前面的文章已经把模板、模型、视图、表单等知识点逐一讲解,大家已经熟悉它们具体用法。但如何将其串联起来还一筹莫展。本篇文章分享我之前做过的一个小项目,帮助大家抹开这一层迷雾。

1 想做什么?

我分享的项目是一个社工库查询系统。大家不要一拿到源码,就直接去阅读。建议先思考下,如果让我来设计社工库查询系统,我要如何实现?

我就先阐述我思考的内容。既然项目是一个查询系统,那么重点在于查询。众所周知,查询数据则是执行 SQL 语句来从数据库中获取数据。SQL 查询语句一般使用表中的字段作为查询条件。哪些能作为查询条件呢?先让我们来看看数据库表是怎么定义的?

2 数据库表

数据库中只有一个表,名为 Socialusers。它的创建语句是:

Create table if not exists `SocialUsers` (
     `id` INTEGER PRIMARY KEY,
     `username` varchar(64),
     `password` varchar(64),
     `chineseName` varchar(64) ,
     `email` varchar(64) ,
     `QQ` varchar(15) ,
     `weibo` varchar(300),
     `identity_number` varchar(25),
     `cell_phone` varchar(20),
     `ip_address` varchar(100),
     `college` varchar(60),
     `living_place` varchar(200),
     `source` varchar(50),
     `remarks` varchar(400)
     );

表中一共有 13 个字段,其中有 9 个字段的内容属于敏感字段,涉及到个人账号安全问题。因此,我将这 9 个字段,分别是 username、password、chineseName、email、QQ、identity_number、cell_phone、college、source 作为查询条件

那么系统最终的效果是根据查询条件和用户输入的内容来查询数据。如果数据库匹配到数据,就通过表格形式打印数据。反之则提醒用户查询不到数据(查询不到数据应该值得庆幸的,说明你的账号数据没有被泄露)。

我将查询条件以下拉框的形式显示,让用户可以自行选择查询条件。查询内容以文章输入框展示,提供用户输入数据的接口。到这里可以回答之前的想要做什么的问题了。我们最终要实现的页面效果如下:


点击查看大图

3 模型建立

数据库已经有了表,我直接使用 Django 自带工具生成 Socialusers 模型。这一步就省略不讲,如果你对这步操作不是很熟悉,可以阅读《Django 学习笔记之使用旧数据库》的内容。

最终生成的模型如下:

class Socialusers(models.Model):
    id = models.IntegerField(blank=True, primary_key=True)
    username = models.CharField(max_length=64, blank=True, null=True)
    password = models.CharField(max_length=64, blank=True, null=True)
    # Field name made lowercase.
    chinesename = models.CharField(db_column='chineseName', max_length=64, blank=True, null=True)  
    email = models.CharField(max_length=64, blank=True, null=True)
    # Field name made lowercase.
    qq = models.CharField(db_column='QQ', max_length=15, blank=True, null=True)  
    weibo = models.CharField(max_length=300, blank=True, null=True)
    identity_number = models.CharField(max_length=25, blank=True, null=True)
    cell_phone = models.CharField(max_length=20, blank=True, null=True)
    ip_address = models.CharField(max_length=100, blank=True, null=True)
    college = models.CharField(max_length=60, blank=True, null=True)
    living_place = models.CharField(max_length=200, blank=True, null=True)
    source = models.CharField(max_length=50, blank=True, null=True)
    remarks = models.CharField(max_length=400, blank=True, null=True)

    class Meta:
        managed = True
        db_table = 'SocialUsers'

注意下 chinesename 和 qq 这两个属性。我们在数据库中的定义是 chineseName 和 QQ。而 Django 在生成的模型时,自动将大写转为化小写。因此,当我们在使用 Socialusers 这两个属性时,要注意它们的定义已经改变,属性的定义全是小写的。

4 表单实现

表单的实现由两种方式:一种是根据 model 生成 Form,另一种是自定义 Form。我分别把这两种方式实现。你可以对比下这两种写法的差异,以及如何给表单指定 bootstrap CSS 样式的。

4.1 根据 model 生成 Form

在 app 目录下新建文件夹 forms 以及文件 forms.py 。forms.py 主要存放表单的定义,实现代码如下:

# forms.py 
CONDITION_CHOICES = (
    ('username', '用户名'),
    ('password', '密码'),
    ('chineseName', '姓名'),
    ('email', '邮箱'),
    ('QQ', 'QQ'),
    ('identity_number', '身份证'),
    ('cell_phone', '电话'),
    ('college', '大学'),
    ('source', '来源'),
)

class QueryUserForm(forms.Form):
    condition = forms.CharField(  # 也可以定义为 ChoiceField
        max_length=20,
        widget=forms.Select(choices=CONDITION_CHOICES,
                            attrs={'class':"form-control",
                                    'title':"query condition",
                                    'name':'condition',
                                    }),
        localize=('username', '用户名')
    )

    queryContent = forms.CharField(
        max_length=100,
        widget=forms.TextInput(attrs={'class': 'form-control is-invalid',
                                      'name': 'queryContent',
                                      'placeholder': '请输入需要要查询的内容...'
                                      }),
        error_messages={'required': '查询内容不能为空 !'}
    )

其中, widget 是指定字段呈现样式,如 condition 被指定呈现下拉框 Select;localize 是设置初始化值;error_messages 是错误提示形式。attrs 是为呈现的组件指定一些属性,如 CSS 样式、name 等。这部分内容,后面的文章会做详细讲解。

4.2 自定义 Form

为了更好区分模型,我在 models.py 中新建一个模型来代替之前的 Socialusers 模型。

# models.py 
# 用于表单查询的 model
CONDITION_CHOICES = (
    ('username', '用户名'),
    ('password', '密码'),
    ('chineseName', '姓名'),
    ('email', '邮箱'),
    ('QQ', 'QQ'),
    ('identity_number', '身份证'),
    ('cell_phone', '电话'),
    ('college', '大学'),
    ('source', '来源'),
)

class QueryUser(models.Model):
    condition = models.CharField(max_length=20, choices=CONDITION_CHOICES)
    queryContent = models.CharField(max_length=100)

    def __str__(self):    # __unicode__ on Python 2
        return self.condition

Form 的实现代码如下:

# forms.py 
class QueryUserForm(ModelForm):
    class Meta:
        model = QueryUser
        fields = ['condition', 'queryContent', ]
        # 指定呈现样式字段、指定 CSS 样式
        widgets = {
            'condition': Select(attrs={'class':"form-control",
                                    'title':"query condition",
                                    'name':'condition',
                                    }),
            'queryContent':TextInput(attrs={'class': 'form-control is-invalid',
                                      'name': 'queryContent',
                                      'placeholder': '请输入需要要查询的内容...'
                                      })
        }

        localized = {
            'condition':('username', '用户名'),
            'queryContent':123
        }

        # 自定义错误信息
        error_messages = {
            'queryContent':{
                'required': '查询内容不能为空 !',
            }
        }

各个字段的含义跟第一种实现方式类似,我就不重复说明。

5 模板创建

我创建名为 templates 文件夹来存放 HTML 文件。其中 index.html 是主页面,也是我们查询数据的页面。因为我前端框架使用的是 bootstrap,所以需要加载一些库。我为了满足在电脑无网络的状态也能使用的需求。我将其 bootstrap 所用到的库到打包到 static 目录下。

代码比较多,我只把重点内容贴出来。详细代码可以到 Github 网站上查看完整代码

{% load staticfiles %}
<!DOCTYPE html>
<html>
<body>
    <div class="container" id="container">
        <div id="page-header">
            <h1 class="text-center"> 社工库查询系统 </h1>
        </div>

        <div class="row">
        <form action="" method="GET" class="form-horizontal" role="form">
            <div id="checkbox" class="text-center">
                <label class="checkbox-inline text-success">默认采用完整匹配</label>
            </div>

            <div class="col-md-10 col-md-offset-1">
                <div class="col-md-2 col-md-offset-0">
                    {{ form.condition }}
                    {% comment %}
                    {{ form.condition }} 在 html 中将被渲染成以下代码
                    <select name='condition' title="query condition" class="form-control">
                        <option >用户名</option>
                        <option>密码</option>
                        <option>姓名</option>
                        <option>邮箱</option>
                        <option>QQ</option>
                        <option>身份证</option>
                        <option>电话</option>
                        <option>大学</option>
                        <option>来源</option>
                    </select>
                    {% endcomment %}
                </div>

                <div class="input-group col-md-10 col-md-offset-1">
                    {{ form.queryContent.field.errors }}
                    {{ form.queryContent }}

                    {% comment %}
                    {{ form.queryContent }} 在 html 中将被渲染成以下代码
                    <input type="text" class="form-control is-invalid" name="q" placeholder="请输入内容..." value="">
                    {% endcomment %}

                    <div class="input-group-btn">
                        <button type="submit" class="btn btn-primary" required >Search</button>
                        <div class="invalid-feedback">
                            Please provide a valid value.
                        </div>
                    </div>
                </div>
            </div>
        </form>
        </div>
        <br>
    </div>
</body>
</html>

在 html 代码中,我直接将 form 标签直接写出来,里面的 Select 和 Input 标签通过 Django Form 来填充。下拉框使用表单的 condition 属性,即 {{ form.condition }} 来填充 ,输入框也是使用 {{ form.queryContent }} 来填充。当它们渲染的时候,会自动被解析为 Select 和 input 控件。

6 视图

我设定是用户提交表单地址不改变。所以表单数据会被提交到原来的页面。因此,在视图的工作是拦截表单,并截取里面的内容。最后将查询结果返回给 HTML 页面。

# views.py
def index(request):
    templateView = 'index.html'

    if request.method == 'GET':
        form = QueryUserForm(request.GET)
        # 验证表单
        if form.is_valid():
            # 过滤需要的数据, 获取查询条件和查询内容
            condition = form.cleaned_data['condition']
            keywords = form.cleaned_data['queryContent']
            print('condition == ' + condition)
            print('keywords == ' + keywords)

            # 查询数据...
            
            return render(request, templateView, {'form': form})
        # 直接访问主页, 显示的内容
        else:
            return render(request, templateView, {'form': form})

系统的基本框架已经搭建差不多。因为文章篇幅关系,一部分内容下篇文章讲解。下篇文章主要是如何查询数据、如何根据查询结果显示不同内容、如何将数据呈现出来。

7 写在最后

我新建一个 Python Web 学习交流 QQ 群,群号:701534112。欢迎大家加群,一起交流,一起学习,一起进步。


Python Web 学习交流群

往前 Django 学习笔记文章
Django 学习笔记之环境搭建
Django 学习笔记之初始
Django 学习笔记之视图与URL配置
Django 学习笔记之模板
Django 学习笔记之模型(上)
Django 学习笔记之模型(下)
Django 学习笔记之后台管理
Django 学习笔记之模型表单
Django 学习笔记之使用旧数据库


本文原创发布于微信公众号「极客猴」,欢迎关注第一时间获取更多原创分享

Django 系列
Web note ad 1