ansheng’s blog!

Django中的递归模型关系

在本文中,我们将使用Django开发一个人力资源(HR)应用程序,并在员工和管理之间建立递归关系。

设置Django项目结构

为了不影响系统环境,你也许会创建一个虚拟环境,当然我已经建立好了虚拟环境,我们开始创建Django项目吧

(test) $ pip install django

安装Django后,可以利用django-admin生成项目模板,我们称之为webapp

(test) $ django-admin startproject webapp

现在cd进入新的webapp目录,我们通过manage.py脚本提供的django工具。我们使用它来创建项目的应用程序,我们将其命名为hrmgmt。这将创建另一个名为hrmgmt的目录,这是该应用程序的代码所在的位置。

(test) $ cd webapp/
(test) $ python manage.py startapp hrmgmt

最后我们还需要将刚创建的hrmgmtAPP注册到django项目的APP列表中

# webapp/settings.py
# Application definition

INSTALLED_APPS = [
    ......
    'hrmgmt.apps.HrmgmtConfig'
]

配置路由

webapp/urls.py中使用以下代码将所有以/hr为前缀的路由重定向到hrmgmt应用程序中

# webapp/urls.py
from django.conf.urls import url, include  
from django.contrib import admin

urlpatterns = [  
    url(r'^admin/', admin.site.urls),
    url(r'^hr/', include('hrmgmt.urls'))
]

hrmgmt应用程序中,创建一个名为urls.py的新文件,并放置以下代码。

# hrmgmt/urls.py
from django.conf.urls import url

from . import views

urlpatterns = [
    # /hr/
    url(r'^$', views.index, name='index')
]

这将指定一个视图,它将返回所有员工的列表。上面的代码使用正则表达式来表示当从我们的服务器请求/hr/的路由时,一个名为index的视图函数应该处理该请求并返回一个响应。

视图

现在我们来实现前面提到的index视图函数来处理对/hr/的路由的请求,并返回一个文本响应。

hrmgmt/views.py中包含以下代码:

# hrmgmt/views.py
from django.http import HttpResponse

def index(request):  
    response = "My List of Employees Goes Here"
    return HttpResponse(response)

在webapp目录下,启动Django并测试我们已经正确配置了路由和查看功能:

(test) $ python manage.py runserver

现在打开浏览器,输入http://127.0.0.1:8000/hr/,就可以看到My List of Employees Goes Here

设计模型类

在本节中,我们定义我们的模型类,它将转换成数据库表,所有这些都通过编写Python代码来完成。

hrmgmt/models.py中放置以下代码:

# hrmgmt/models.py
from django.db import models

class Employee(models.Model):  
    STANDARD = 'STD'
    MANAGER = 'MGR'
    SR_MANAGER = 'SRMGR'
    PRESIDENT = 'PRES'

    EMPLOYEE_TYPES = (
        (STANDARD, 'base employee'),
        (MANAGER, 'manager'),
        (SR_MANAGER, 'senior manager'),
        (PRESIDENT, 'president')
    )

    role = models.CharField(max_length=25, choices=EMPLOYEE_TYPES)
    first_name = models.CharField(max_length=100)
    last_name = models.CharField(max_length=100)
    manager = models.ForeignKey('self', null=True, related_name='employee')

    def __str__(self):
        return "<Employee: {} {}>".format(self.first_name, self.last_name)

    def __repr__(self):
        return self.__str__()

在上面的代码中,我们创建了一个django的modelEmployee,该类具有Django的ORM访问数据库的功能。

接下来是四个类字段的定义,它们是常量(STANDARD,MANAGER,SR_MANAGER,PRESIDENT)以及它们用于进一步定义元组类字段常量的定义。这些类似于枚举,它们指定了员工可以承担的不同角色。

接下来first_namelast_name字段被定义为最大长度为100个字符的字符。

最后一个字段定义员工与管理之间递归关系的外键,这意味着Django对继承的模型所做的隐式自动递增整数iddjango.db.models.Model将作为同一个类(或表)的外键值提供。

生成数据表

运行以下命令创建所有Django应用程序使用的表。默认情况下,数据库使用的是sqlite

(test) $ python manage.py makemigrations
(test) $ python manage.py migrate

您可以通过运行以下命令来查看创建表的实际SQL:

(test) $ python manage.py sqlmigrate hrmgmt 0001
BEGIN;
--
-- Create model Employee
--
CREATE TABLE "hrmgmt_employee" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "role" varchar(25) NOT NULL, "first_name" varchar(100) NOT NULL, "last_name" varchar(100) NOT NULL, "manager_id" integer NULL REFERENCES "hrmgmt_employee" ("id"));
CREATE INDEX "hrmgmt_employee_manager_id_43028de6" ON "hrmgmt_employee" ("manager_id");
COMMIT;

用Django Shell创建数据

输入以下命令,进入带Django环境的Python解释器:

(test) $ python manage.py shell

下面的代码创建了四名员工。

>>> from hrmgmt.models import Employee
>>> janeD = Employee.objects.create(first_name='Jane', last_name='Doe', role=Employee.PRESIDENT)
>>> johnD = Employee.objects.create(first_name='John', last_name='Doe', role=Employee.MANAGER, manager=janeD)
>>> joeS = Employee.objects.create(first_name='Joe', last_name='Scho', role=Employee.STANDARD, manager=johnD)
>>> johnB = Employee.objects.create(first_name='John', last_name='Brown', role=Employee.STANDARD, manager=johnD)

创建显示模板

hrmgmt目录中创建另一个名为templates的目录。然后在templates目录下再创建一个名为hrmgmt的目录。最后在hrmgmt/templates/hrmgmt目录下创建一个名为index.html的HTML文件。在这个文件中,我们将编写代码来构建我们的员工列表。

代码如下:

<!-- hrmgmt/templates/hrmgmt/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <title>Employee Listing</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous">
    <script src="https://cdn.bootcss.com/bootstrap/4.0.0-beta/js/bootstrap.min.js" integrity="sha384-h0AbiXch4ZDo7tp9hKZ4TsHbi047NrKGLO3SEJAg45jXxnGIfYzk4Si90RDIqNm1" crossorigin="anonymous"></script>
</head>
<body>
<div class="container">
    <div class="row">
        <div class="col-md-12">
            <h1>Employee Listing</h1>
        </div>
    </div>
    <div class="row">
        <dov class="col-md-12">
            <table class="table table-striped">
                <thead class="thead-inverse">
                <tr>
                    <th>Employee ID</th>
                    <th>First Name</th>
                    <th>Last Name</th>
                    <th>Role</th>
                    <th>Manager</th>
                </tr>
                </thead>
                <tbody class='table-striped'>
                
                </tbody>
            </table>
        </dov>
    </div>
</div>
<script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script src="https://cdn.bootcss.com/popper.js/1.11.0/umd/popper.min.js" integrity="sha384-b/U6ypiBEHpOf/4+1nzFpr53nxSS+GLCkfwBdFNTxtclqqenISfwAzpKaMNFNmj4" crossorigin="anonymous"></script>
</body>
</html>

我们需要在view中获取所有的员工数据,然后传递给html模板中。

# hrmgmt/views.py
from django.shortcuts import render

from .models import Employee


def index(request):
    employees = Employee.objects.order_by('id').all()
    context = {'employees': employees}
    return render(request, 'hrmgmt/index.html', context)

再次启动Django服务,打开浏览器输入http://127.0.0.1:8000/hr/,你将会看到如下截图:

1507689271591157