Использование Django ManyToManyField Through с примерами

В отношении «многие ко многим» несколько строк в таблице связаны с несколькими строками в другой таблице. Для установления отношения «многие ко многим» реляционные базы данных используют третью таблицу, называемую таблицей соединения, и создают два отношения «один ко многим» из исходных таблиц.

Обычно таблица соединений содержит значения идентификаторов исходных таблиц, чтобы строки в одной таблице могли соотноситься со строками в другой таблице.

Иногда вам может понадобиться добавить дополнительные поля в таблицу соединений. Например, каждый сотрудник может иметь несколько мест работы в течение своей карьеры. Чтобы отслеживать, когда сотрудник приступает к работе, вы можете добавить поля begin_date и end_date в таблицу соединений.

Чтобы сделать это в Python, используйте ManyToManyField в Django с аргументом through.

Например, ниже показано, как связать сотрудника с несколькими должностями с помощью назначений:

class Employee(models.Model):
   # ...

class Job(models.Model):
    title = models.CharField(max_length=255)
    employees = models.ManyToManyField(Employee, through='Assignment')

    def __str__(self):
        return self.title


class Assignment(models.Model):
    employee = models.ForeignKey(Employee, on_delete=models.CASCADE)
    position = models.ForeignKey(Job, on_delete=models.CASCADE)
    begin_date = models.DateField()
    end_date = models.DateField(default=date(9999, 12, 31))

Как это работает.

  • Сначала определите модель Job, добавьте атрибут employees, который использует ManyToManyField, и передайте Assignment в качестве сквозного аргумента.
  • Во-вторых, определите класс Assignment, который имеет два внешних ключа, один из которых ссылается на модель Employee, а другой — на модель Job. Также добавьте атрибуты begin_date и end_date в модель Assignment.

Запустите makemigrations, чтобы создать новые миграции:

python manage.py makemigrations

Выход:

Migrations for 'hr':
  hr\migrations\0005_assignment_job_assignment_job.py
    - Create model Assignment
    - Create model Job
    - Add field job to assignment

И выполните команду миграции, чтобы применить изменения к базе данных:

python manage.py migrate

Выход:

Operations to perform:
  Apply all migrations: admin, auth, contenttypes, hr, sessions
Running migrations:
  Applying hr.0005_assignment_job_assignment_job... OK

За кулисами Django создает таблицы hr_job и hr_assignment в базе данных:

Пример создания таблиц hr_job и hr_assignment в базе данных

Таблица hr_assignment — это объединяемая таблица. Помимо полей employee_id и position_id, она содержит поля begin_date и end_date.

Содержание

Создание новых рабочих мест

Сначала выполните команду shell_plus:

python manage.py shell_plus

Во-вторых, создайте три новые должности:

>>> j1 = Job(title='Software Engineer I')
>>> j1.save()
>>> j2 = Job(title='Software Engineer II') 
>>> j2.save() 
>>> j3 = Job(title='Software Engineer III')
>>> j3.save()
>>> Job.objects.all()
<QuerySet [<Job: Software Engineer I>, <Job: Software Engineer II>, <Job: Software Engineer III>]>

Создание экземпляров для промежуточных моделей

Сначала найдите сотрудников с именами John Doe и Jane Doe:

>>> e1 = Employee.objects.filter(first_name='John',last_name='Doe').first()
>>> e1
<Employee: John Doe>
>>> e2 = Employee.objects.filter(first_name='Jane', last_name='Doe').first()
>>> e2
<Employee: Jane Doe>

Во-вторых, создайте экземпляры промежуточной модели (Assignment):

>>> from datetime import date
>>> a1 = Assignment(employee=e1,job=j1, begin_date=date(2019,1,1), end_date=date(2021,12,31))
>>> a1.save()
>>> a2 = Assignment(employee=e1,job=j2, begin_date=date(2022,1,1))
>>> a2.save()
>>> a3 = Assignment(employee=e2, job=j1, begin_date=date(2019, 3, 1))
>>> a3.save()

В-третьих, найдите сотрудников, занимающих должность Software Engineer I (p1):

>>> j1.employees.all()
<QuerySet [<Employee: John Doe>, <Employee: Jane Doe>]>

За кулисами Django выполняет следующий запрос:

SELECT
  "hr_employee"."id",
  "hr_employee"."first_name",
  "hr_employee"."last_name",
  "hr_employee"."contact_id",
  "hr_employee"."department_id"
FROM "hr_employee"
INNER JOIN "hr_assignment"
  ON("hr_employee"."id" = "hr_assignment"."employee_id")
WHERE "hr_assignment"."job_id" = 1

Аналогичным образом вы можете найти всех сотрудников, занимающих должность Software Engineer II:

>>> j2.employees.all()
<QuerySet [<Employee: John Doe>]>

Удаление экземпляров промежуточной модели

Сначала удалите Jane Doe (e2) из вакансии «Software Engineer II» с помощью метода remove():

>>> j2.employees.remove(e2)

Во-вторых, удалите всех сотрудников с должности Software Engineer I с помощью метода clear():

>>> j1.employees.clear()

На данный момент на вакансии j1 не должно быть сотрудников:

>>> j1.employees.all() 
<QuerySet []>
Похожие посты
Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *