Рубрики
Без рубрики

Рекурсивные модельные отношения в Django

Автор оригинала: Adam McQuistan.

Рекурсивные модельные отношения в Django

Необходимость рекурсивных отношений

При разработке современных веб-приложений часто возникают ситуации, когда бизнес-требования по своей сути описывают отношения, которые являются рекурсивными . Одним из хорошо известных примеров такого бизнес-правила является описание сотрудников и их отношения к своим менеджерам, которые также являются сотрудниками. Обратите внимание на цикличность этого утверждения. Это именно то, что подразумевается под рекурсивным отношением. В этой статье мы будем разрабатывать демонстрацию голых костей в Django приложения для листинга сотрудников отдела кадров (HR) с этой рекурсивной связью между сотрудниками и менеджерами.

Код для этой статьи можно найти в этом репо GitHub .

Настройка структуры проекта Django

Чтобы начать работу с проектом Django, вам нужно создать новую виртуальную среду Python (предпочтительно Python3). Если вы не знакомы с виртуальными средами, пожалуйста, ознакомьтесь с этой статьей . Как только вы окажетесь в активированной виртуальной среде, pip установит Django.

(venv) $ pip install django

С установленным Django вы можете использовать утилиты администратора Django для создания шаблона проекта, который мы назовем “webapp”. Подробнее о настройке проекта Django вы можете узнать в нашей статье Flask vs Django .

(venv) $ django-admin startproject webapp

Теперь cd в новый каталог webapp, чтобы мы могли в дальнейшем использовать другой набор инструментов Django через manage.py сценарий. Мы используем это для создания приложения вашего проекта, которое мы назовем “hr mgmt”. Это создает еще один каталог под названием “hr mgmt”, в котором будет находиться код для этого приложения.

(venv) $ cd webapp
(venv) $ python manage.py startapp hrmgmt

Последняя часть настройки проекта включает в себя информирование проекта (веб-приложения) о приложении “hr mgmt”. В “webapp/settings.py” найдите раздел с комментарием “Определение приложения” над списком INSTALLED_APPS и добавьте запись hrmgmt.apps.HrmgmtConfig , вот так:

# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'hrmgmt.apps.HrmgmtConfig'
]

Настройка маршрутов

В Django каталог, соответствующий названию проекта, в нашем случае “webapp”, – это место, где находятся основные настройки и точки входа в маршруты для встроенного приложения администратора и любых дополнительных пользовательских приложений. Так что в “webapp/urls.py” используйте следующий код, чтобы направить все маршруты с префиксом “/hr” в приложение “hr mgmt”.

# 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'))
]

В пользовательском приложении “hr mgmt” создайте новый файл с именем “urls.py-и поставьте следующий код. Это указывает представление, которое будет возвращать список всех сотрудников. Приведенный ниже код использует регулярное выражение, чтобы указать, что когда маршрут “/hr/” запрашивается с нашего сервера, то функция представления с именем index должна обработать запрос и вернуть ответ.

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

import views

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

Далее мы поговорим о том, что делает функция индексного представления.

Заглушение функции индексного представления

Теперь давайте реализуем вышеупомянутую функцию index view для обработки запросов на маршрут “/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 и проверьте, правильно ли мы настроили наш маршрут и функцию просмотра:

(venv) $ python manage.py runserver

Теперь зайдите в свой браузер и введите http://localhost:8000/hr/ и вы должны увидеть текстовый ответ “Мой список сотрудников идет сюда”

Проектирование наших классов моделей

Наконец-то мы добрались до хорошей части! В этом разделе мы определяем наши классы моделей, которые будут преобразовываться в таблицы базы данных, все это делается путем написания кода Python. Или используя что то .NET folks придумали как “кодовый первый” подход к проектированию баз данных.

В 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 "".format(self.first_name, self.last_name)

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

В этих нескольких строках кода есть довольно много всего, так что давайте разберем их. Первое, что следует отметить, это то, что объявляется класс Python с именем Employee , который наследуется от django.db.models.Модель класс. Это наследование дает классу Employee функциональность для доступа к базе данных через ORM Django.

Далее приведены определения четырех полей класса, которые являются константами (STANDARD, MANAGER, SR_MANAGER, PRESIDENT), и их использование для дальнейшего определения константы поля класса кортежа. Это что-то вроде перечислений, которые определяют различные роли, которые может взять на себя сотрудник. Фактически, константа кортежа кортежей передается в определение поля класса ролей, чтобы указать, какие значения классу должно быть разрешено принимать.

Далее поля first_name и last_name class определяются как символьные поля с максимальной длиной 100 символов.

Последнее определяемое поле, возможно, является наиболее значимым-поле manager . Это внешний ключ, который определяет рекурсивные отношения между сотрудниками и их менеджерами. Это означает, что неявный автоинкрементный столбец integer id, который Django создает для моделей, наследуемых от django.db.models.Model будет доступен в качестве значения внешнего ключа для того же класса (или таблицы).

Это удовлетворит наш вариант использования, который можно сформулировать так: “У сотрудника может быть только один прямой менеджер или нет менеджера в случае президента, но сотрудник может управлять многими разными сотрудниками”. Указав self в качестве первого параметра модели .ForeignKey call, Django установит это как рекурсивное отношение. Затем, указав null=True , модель допустит наличие сотрудника без руководителя, который в нашем примере представляет президента.

Ниже приведена диаграмма ERD рекурсивного отношения, которое мы определили.

Перенос определения класса в базу данных

Для того чтобы преобразовать код, который мы использовали для определения нашего класса Employee, в DDL SQL, мы снова воспользуемся утилитой Django, доступ к которой осуществляется через “manage.py” сценарий и все вместе известные как миграции.

В командной строке, конечно же, в нашей виртуальной среде, выполните следующие действия, чтобы создать таблицы по умолчанию, которые используют все приложения Django. По умолчанию эта база данных является базой данных sqlite в корневой папке проекта.

(venv) $ python manage.py migrate

После завершения мы можем сделать новую миграцию, которая определяет таблицу, которая будет поддерживать наш класс Employee . Сделайте это, выполнив следующие команды и убедитесь, что вы наблюдаете за выводом, как показано ниже:

(venv) $ python manage.py makemigrations
(venv) $ python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, hrmgmt, sessions
Running migrations:
  Applying hrmgmt.0001_initial... OK

Вы можете просмотреть фактический DDL SQL, который создает таблицу, выполнив приведенную ниже команду:

(venv) $ 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

В командной строке введите следующую команду, чтобы запустить интерпретатор с контекстом нашего приложения Django, предварительно загруженным в REPL:

(venv) $ python manage.py shell

Теперь, когда интерпретатор Python запущен и запущен, введите следующие команды:

>>> 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)

Вышеприведенный код создает четырех фиктивных сотрудников. Джейн Доу – президент. Затем у Джона Доу есть роль менеджера, и им управляет его мать Джейн Доу (да, здесь явно присутствует некоторая кумовство). Под наблюдением Джона Доу находятся Джо Шмо и Джон Браун, которые оба выполняют роль стандартного или базового сотрудника.

Мы можем проверить наше поле отношений employee , проверив выходные данные вызова employee на нашей johnD переменной:

>>> johnD.employee.all()
, ]>

А также с переменной jane :

>>> janeD.employee.all()
]>

Точно так же мы хотим протестировать наше поле менеджера, чтобы убедиться, что оно работает так, как требуется:

>>> johnD.manager

Отлично! Похоже, все идет так, как и ожидалось.

Настройка Нашего Вида

В том же каталоге, что и наш каталог “hr mgmt”, создайте еще один каталог под названием “шаблоны”. Затем в каталоге “шаблоны” создайте еще один каталог под названием “hr mgmt”. Наконец, в каталоге “hr mgmt/templates/hr mgmt” создайте HTML-файл с именем “index.html”. Именно в этом файле мы напишем код для построения нашего списка сотрудников.

Скопируйте и вставьте следующий код:





    
        Employee Listing
        
        
        
        
    
    
        

Employee Listing

{% for employee in employees %} {% endfor %}
Employee ID First Name Last Name Role Manager
{{ employee.id }} {{ employee.first_name }} {{ employee.last_name }} {{ employee.get_role_display }} {% if employee.manager %}{{ employee.manager.first_name }} {{ employee.manager.last_name }}{% endif %}

Этот файл известен как template в веб-фреймворке Django. Шаблоны представляют собой чертеж воспроизводимого HTML-кода, который динамически генерируется на основе передаваемых ему данных. В нашем случае данные, передаваемые в наш шаблон “индекс”, представляют собой наш список сотрудников.

Чтобы обслуживать наш шаблон, нам нужно будет внести несколько изменений в нашу функцию просмотра. А именно, нам нужно импортировать вспомогательную функцию render из ярлыков Django, а затем вместо возврата HttpResponse мы вернем вызов render , передав объект request , путь к нашему шаблону и словарь, содержащий данные для передачи в наш шаблон.

# 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://localhost:8000/hr/ в поле URL-адрес нажмите кнопку “Enter”. Вы должны увидеть результат, похожий на следующий скриншот:

В результирующем столбце таблицы “Менеджер” вы можете видеть, что мы успешно связали Employee с Employee с помощью моделей Django.

Вывод

В этой статье мы рассмотрели пример использования, почему мы должны реализовать рекурсивные отношения в модели Django. Мы прошлись по коду для определения такой рекурсивной связи, а также по тому, как взаимодействовать с моделями, чтобы сохранить их в базе данных, а затем как их извлечь. Наконец, мы завершили работу, увидев, как отображать информацию в наших моделях, поддерживаемых базой данных, в шаблоне Django.

Если вы зашли так далеко, я хотел бы поблагодарить вас за то, что вы прочитали мою статью. Я надеюсь, что эта статья вдохновит вас на дальнейшее изучение веб-разработки с помощью веб-фреймворка Django. Как всегда, я приглашаю любые комментарии, предложения или критические замечания.