模型层构建——python工业互联网应用实战

本章会讲到如何从需求分析获得的实体数据转到模型设计中来,变成一Django 项目中的模型层,正式进入到实战项目开发过程,当然,第一步还是在 VS2019 IDE 环境中创建一个工程项目,本文我们把工程名称命名为 IndDemo ,如下图:

模型层构建——python工业互联网应用实战

VS2019 创建的 Django 项目结构如下图

模型层构建——python工业互联网应用实战

现在按 F5 调试程序,又来到 Django 熟悉的欢迎页面了。

模型层构建——python工业互联网应用实战

接下来让开始这趟实战之旅吧 !

1.1.实体关系图

从上一章需求涉及到的实体,来构建我们的实体关系图吧,这里的步骤也相当于早期开发设计的表结构设计,只是如前面章节说的有了 ORM 机制后,我们讨论采用对象模型来设计和讨论实体可能更适合于实际的场景讨论,便于团队在同一个频道下沟通,当然这里实质上与表设计没有太多本质的区别,但是多对多等中间表在实体关系图中就看不到了,表结构设计时就不能忽略这个中间表的存在。产品经理更专注于需求而不是数据库存储结构是不是更符合专业分工?开发人员也不用大费周章来折腾表结构的优化了。嗯,这些可以丢给 DBA 来优化吧。

模型层构建——python工业互联网应用实战

本图采用网址 ponyorm 网站在线绘制,网址: https://editor.ponyorm.com/ .

1.2. Django model 层

为了便于对照学习和理解,我们新增一个新的 APP 命名为: Task 来专门处理这个业务,不修改默认的 APP 里的代码,也便于过程中可以通过这个默认 APP 功能,参照它的结构和语法等。

模型层构建——python工业互联网应用实战

现在我们的工程目录结构如下:

模型层构建——python工业互联网应用实战

打开 Task.models.py 文件编写我们实体关系图的模型代码吧,作者本人比较习惯“大驼峰”命名法也就是首字母都大写的模式 TaskNum ,后面很多命名都会采用这个方式,不采用 python 主流的蛇形命名法的 task_num 方式,纯属个人习惯。我们依据设计我们开始构建我们的 model 代码如下:

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

PRIORITY=((1,u'正常'),(2,u'急'),(3,u'紧急'))

class Task(models.Model):
    TASK_STATE=((1,u'未处理'),(4,u'处理成功'),(5,u'执行中'),(99,u'完成'),(-1,u'已取消'))

    TaskId = models.AutoField(primary_key=True, db_column='task_id')
    TaskNum = models.IntegerField(u'任务号', null=False, db_column='task_num')
    Source = models.CharField(u'源地址', null=False, max_length=50, db_column='source')
    Target = models.CharField(u'目标地址', null=False, max_length=50, db_column='target')
    Barcode = models.CharField(u'容器条码', null=False, max_length=50, db_column='barcode')
    State = models.IntegerField(u'状态', choices=TASK_STATE, null=False, db_column='state')
    Priority = models.IntegerField(u'优先级', choices=PRIORITY, null=True, db_column='priority')
    BeginDate = models.DateTimeField(u'开始时间',null=True, db_column='begin_date')
    EndDate = models.DateTimeField(u'结束时间',null=True, db_column='end_date')
    SystemDate = models.DateTimeField(u'系统时间', null=False, auto_now_add=True, db_column='system_date')
    User = models.ForeignKey(User, verbose_name="操作员", on_delete=models.CASCADE,db_column='user_id')

    class Meta:
        db_table = 'task_task'
        ordering = ['-Priority','TaskId']
        verbose_name = verbose_name_plural = "任务"

    def __str__(self):
        return str(self.TaskNum)


class Job(models.Model):
    JOB_STATE=((1,u'新作业'),(2,u'下达执行'), (99,u'完成'),(-1,u'已取消'))

    JobId = models.AutoField(primary_key=True, db_column='job_id')
    Task = models.ForeignKey('Task', verbose_name="任务", blank=True, on_delete=models.CASCADE)
    TaskNum = models.IntegerField(u'任务号', null=False, db_column='task_num')
    OrderNo = models.IntegerField(u'顺序号', null=False, db_column='order_no')
    Source = models.CharField(u'源地址', null=True, max_length=50, db_column='source')
    Target = models.CharField(u'目标地址', null=True, max_length=50, db_column='target')
    Executor = models.CharField(u'执行器', null=False,blank=True, max_length=50, db_column='executor')
    State = models.IntegerField(u'状态', choices=JOB_STATE, null=False, db_column='state')
    Priority = models.IntegerField(u'优先级', choices=PRIORITY, null=True, db_column='priority')
    BeginDate = models.DateTimeField(u'开始时间',null=True, db_column='begin_date')
    EndDate = models.DateTimeField(u'结束时间',null=True, db_column='end_date')
    SystemDate = models.DateTimeField(u'系统时间', null=False, auto_now_add=True, db_column='system_date')
    Barcode = models.CharField(u'条码', null=False, max_length=50, db_column='barcode')
    User = models.ForeignKey(User, verbose_name="操作员", on_delete=models.CASCADE,db_column='user_id')

    class Meta:
        db_table = 'task_job'
        ordering = ['JobId']
        verbose_name = verbose_name_plural = "作业"

    def __str__(self):
        return str(self.TaskNum) +'-'+str(self.OrderNo)

1.3. make migrations 创建数据库迁移

首先,我们在项目的 settings.py 文件里登记我们新增加的 APP ,代码如下:

INSTALLED_APPS = [
    'app',
    'Task',
    # Add your apps here to enable them
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

然后,在 IDE 通过菜单就可以创建新建 model 产生的数据库迁移,如下图:

模型层构建——python工业互联网应用实战

模型层构建——python工业互联网应用实战

1.4.migrate迁移数据库

同样在 IDE 窗口可以直接执行迁移命令,执行完数据迁移命令,工程默认链接的 SQLite 数据库就包含这次我们创建的数据表了。

模型层构建——python工业互联网应用实战

通过数据库工具查看数据文件里面的表创建情况。

模型层构建——python工业互联网应用实战

1.5.Create Superuser

由于我们的 model Task 中定义了 User 这个引用 django.contrib.auth.models.User 的外键非空属性,我们需要创建一个 Django Admin 的超级用户,才能正常的保存 Task 模型。

模型层构建——python工业互联网应用实战

按照命令提示创建好超级用户我们就可以通过 ORM 操作数据库了。

1.6. Django ORM 操作

Django ORM 提供了诸多的 API 接口,详情请参考官网文档或网上教程,笔者的项目实战也是遇到某类问题了,也是大量通过搜索引擎来找到相关的使用方法来改进功能代码的。 实战项目使用到 ORM 的操作很多,未来我们会逐步的使用到时,会进一步说明。本章节只举一些常用的例子。

通过 ORM 我们可以不关心数据库表结构,采用 ORM 机制就可以 CRUD 表里的数据了,数据将按照对象的方式的方式返回。

模型层构建——python工业互联网应用实战

1.返回所有的 Task 对象

>>> from Task.models import Task

>>> Task.objects.all()

<QuerySet []>  

2.新增 Task 对象

>>> a=Task(None,'1001','121','1-1-1001','2002001',1,1,None,None,None,1)

>>> a.save()

>>> Task.objects.all()

<QuerySet [<Task: 1001>]>

通过数据库工具也能看到这条新创建的 Task

模型层构建——python工业互联网应用实战

3.通过 TaskId 获取 Task 对象

>>> Task.objects.filter(pk=1)

<QuerySet [<Task: 1001>]>
>>> Task.objects.get(TaskId=1)

<Task: 1001>

注意 上面 get 方法的两个不同的属性

4. 通过 dict 对象创建 Task 对象,这个模式后面用在以 Json 格式从前台提交数据会非常方便

>>>data={"TaskNum":"1002","Source":"122","Target":"1-1-1002","Barcode":"2002002","State":1,"Priority":1,"User_id":1}

>>> model=Task(**data)

>>> model.save()

>>> Task.objects.all()

<QuerySet [<Task: 1001>, <Task: 1002>]>

注意:User 外键对象必须通过 _id赋值"User_id":1, 通常在变量前加一个星号 (*) 表示这个变量是元组 / 列表,加两个星号表示这个参数是字典。

5.匹配条件

>>> Task.objects.filter(TaskNum='1001')

<QuerySet [<Task: 1001>]>

6.不匹配条件

>>> Task.objects.exclude(TaskNum='1001')

<QuerySet [<Task: 1002>]>

7.获取单条数据(主键唯一)

>>> Task.objects.get(pk=1)

<Task: 1001>

>>> Task.objects.get(TaskId=1)

<Task: 1001> 

8.大于  __gt

>>> Task.objects.filter(pk__gt=1)

<QuerySet [<Task: 1002>]>

9.大于等于 __gte

>>> Task.objects.filter(pk__gte=1)

<QuerySet [<Task: 1001>, <Task: 1002>]>

10. 小于 __lt

>>> Task.objects.filter(pk__lt=2)

<QuerySet [<Task: 1001>]>

11.小于等于 __lte

>>> Task.objects.filter(pk__lte=2)

<QuerySet [<Task: 1001>, <Task: 1002>]>

12. 大于和小于, 1 < TaskId < 5

>>> Task.objects.filter(pk__gt=1,pk__lt=5)

<QuerySet [<Task: 1002>]>

13.包含 __in []

>>> Task.objects.filter(pk__in=[1,2,3,4,5])

<QuerySet [<Task: 1001>, <Task: 1002>]>

14. 不包含, not in

>>> Task.objects.exclude(pk__in=[2,3,4,5])

<QuerySet [<Task: 1001>]>

15. 为空: isnull=True

>>> Task.objects.filter(BeginDate__isnull=True)

<QuerySet [<Task: 1001>, <Task: 1002>]>

16.匹配,大小写敏感 __contains

>>> Task.objects.filter(Barcode__contains="2001")

<QuerySet [<Task: 1001>]>

17. 匹配,大小写不敏感 __icontains

>>> Task.objects.filter(Barcode__icontains="2001")

<QuerySet [<Task: 1001>]> 

18. 范围 ,__range

>>> Task.objects.filter(pk__range=[2,6])

<QuerySet [<Task: 1002>]>

19. 排序, order by

>>> Task.objects.all().order_by('pk')

<QuerySet [<Task: 1001>, <Task: 1002>]> 

20.倒排序, order by

>>> Task.objects.all().order_by('-pk')

<QuerySet [<Task: 1002>, <Task: 1001>]> 

更多的 ORM 查询操作查阅官网文档,上面是业务编程过程用得比较多的一些过滤查询方法。

1.7.  小节

本章我们着重在通过构建实体和实体关系图,实体的属性通常来自业务需求分析和设计时增加的一些必要的字段,如:时间戳、创建对象的用户等就是为了便于查找、定位、过滤数据,以及业务管理上考虑必要增加的一些多出来的实体属性。这里笔者由于历史习惯,表字段命名又采用蛇形命名法,所以增加了一个强制字段名称的属性 db_column= 'user_id' 都是个人习惯缘故,如果全部采用蛇形命名法这个属性就可以去掉了。完成了 model 的构建后,我们就可以进行展开业务逻辑的开发了。