Django ManyToManyField Through

Resumo : neste tutorial, você aprenderá como adicionar campos extras usando ManyToManyFieldo argumento through.

Em um relacionamento muitos para muitos , várias linhas de uma tabela estão associadas a várias linhas de outra tabela. Para estabelecer um relacionamento muitos para muitos, os bancos de dados relacionais usam uma terceira tabela chamada tabela de junção e criam dois relacionamentos um para muitos a partir das tabelas de origem.

Normalmente, a tabela de junção contém os valores de id das tabelas de origem para que as linhas de uma tabela possam estar relacionadas às linhas da outra tabela.

Às vezes, você pode querer adicionar campos extras à tabela de junção. Por exemplo, cada funcionário pode ter vários empregos durante sua carreira.

Para rastrear quando um funcionário aceita um emprego, você pode adicionar os campos begin_datee end_dateà tabela de junção.

Para fazer isso no Django, você usa o ManyToManyFieldcom o throughargumento.

Por exemplo, o seguinte mostra como associar um funcionário a vários cargos por meio de atribuições:

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))Linguagem de código:  Python  ( python )

Como funciona.

  • Primeiro, defina o Jobmodelo, adicione o employeesatributo que usa ManyToManyFielde passe Assignmentcomo throughargumento.
  • Segundo, defina a Assignmentclasse que possui duas chaves estrangeiras, uma vinculada ao Employeemodelo e a outra vinculada ao Jobmodelo. Além disso, adicione os atributos begin_datee end_dateao Assignmentmodelo.

Execute o makemigrationspara fazer novas migrações:

python manage.py makemigrationsLinguagem de código:  Python  ( python )

Saída:

Migrations for 'hr':
  hr\migrations\0005_assignment_job_assignment_job.py
    - Create model Assignment
    - Create model Job
    - Add field job to assignmentLinguagem de código:  Python  ( python )

E execute o migratecomando para aplicar as alterações ao banco de dados:

python manage.py migrateLinguagem de código:  Python  ( python )

Saída:

Operations to perform:
  Apply all migrations: admin, auth, contenttypes, hr, sessions
Running migrations:
  Applying hr.0005_assignment_job_assignment_job... OKLinguagem de código:  Python  ( python )

Nos bastidores, o Django cria as tabelas hr_jobe hr_assignmentno banco de dados:

Esta hr_assignmenté a tabela de junção. Além dos campos employee_ide position_id, possui os campos begin_datee end_date.

Criando novos empregos

Primeiro, execute o shell_pluscomando:

python manage.py shell_plusLinguagem de código:  Python  ( python )

Em segundo lugar, crie três novos cargos:

>>> 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>]>Linguagem de código:  Python  ( python )

Criando instâncias para os modelos intermediários

Primeiro, encontre o funcionário com o nome John Doee 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>Linguagem de código:  Python  ( python )

Segundo, crie instâncias do modelo intermediário ( 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()Linguagem de código:  Python  ( python )

Terceiro, encontre os funcionários que ocupam o Software Engineer Icargo ( p1):

>>> j1.employees.all()
<QuerySet [<Employee: John Doe>, <Employee: Jane Doe>]>Linguagem de código:  Python  ( python )

Nos bastidores, o Django executa a seguinte consulta:

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" = 1Linguagem de código:  Python  ( python )

Da mesma forma, você pode encontrar todos os funcionários que ocupam o Software Engineer IIcargo:

>>> j2.employees.all()
<QuerySet [<Employee: John Doe>]>Linguagem de código:  Python  ( python )

Removendo instâncias das instâncias do modelo intermediário

Primeiro, remova Jane Doe( e2) do Software Engineer IItrabalho usando o remove()método:

>>> j2.employees.remove(e2) Linguagem de código:  Python  ( python )

Segundo, remova todos os funcionários do Software Engineer Itrabalho usando o clear()método:

>>> j1.employees.clear() Linguagem de código:  Python  ( python )

O j1trabalho não deve ter nenhum funcionário agora:

>>> j1.employees.all() 
<QuerySet []>Linguagem de código:  CSS  ( css )

Baixe o código fonte

Resumo

  • Use o throughargumento para ManyToManyFieldadicionar campos extras ao relacionamento muitos para muitos.

Deixe um comentário

O seu endereço de email não será publicado. Campos obrigatórios marcados com *