Resumo : neste tutorial, você aprenderá sobre o relacionamento um-para-um do Django e como ele funciona nos bastidores.
Este tutorial começa onde o tutorial Django ORM parou.
Introdução ao relacionamento Django One-To-One
O relacionamento um-para-um define um link entre duas tabelas, onde cada linha de uma tabela aparece uma vez em outra tabela.
Por exemplo, cada funcionário possui um contato e cada contato pertence a um funcionário. Portanto, o relacionamento entre funcionários e contatos é um relacionamento individual.
Para criar um relacionamento um-para-um, você usa a OneToOneField
classe:
OneToOneField(to, on_delete, parent_link=False, **options)
Linguagem de código: Python ( python )
Nesta sintaxe:
to
parâmetro define o nome do modelo.on_delete
especifica uma ação no contato quando o funcionário é excluído.
O exemplo a seguir usa uma OneToOneField
classe para definir um relacionamento um-para-um entre modelos Contact
e Employee
no models.py
:
from django.db import models
class Contact(models.Model):
phone = models.CharField(max_length=50, unique=True)
address = models.CharField(max_length=50)
def __str__(self):
return self.phone
class Employee(models.Model):
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
contact = models.OneToOneField(Contact, on_delete=models.CASCADE)
def __str__(self):
return f'{self.first_name} {self.last_name}'
Linguagem de código: Python ( python )
A Employee
classe possui o contact
atributo que faz referência a uma instância da OneToOneField
classe.
No OneToOneField
, especificamos o Contact
modelo e a on_delete
opção que define o comportamento quando um objeto funcionário é excluído.
A opção on_delete
= models.CASCADE
significa que se um Employee
objeto for excluído, o Contact
objeto associado a ele Employee
também será excluído automaticamente.
Observe que o Django cria uma restrição de chave estrangeira no banco de dados sem ON DELETE CASCADE
opção. Em vez disso, o Django trata da exclusão manualmente na aplicação. Observe que esta implementação interna pode mudar no futuro.
Migrando modelos para o banco de dados
Primeiro, faça migrações usando o makemigrations
comando:
python manage.py makemigrations
Linguagem de código: Python ( python )
Migrations for 'hr':
hr\migrations\0002_contact_employee_contact.py
- Create model Contact
- Add field contact to employee
Linguagem de código: Python ( python )
Segundo, aplique as migrações ao banco de dados usando o migrate
comando:
python manage.py migrate
Linguagem de código: Python ( python )
Operations to perform:
Apply all migrations: admin, auth, contenttypes, hr, sessions
Running migrations:
Applying hr.0002_contact_employee_contact... OK
Linguagem de código: Python ( python )
Nos bastidores, o Django cria duas tabelas hr_contact
e hr_employee
no banco de dados:
A hr_employee
tabela possui a contact_id
coluna que é uma chave estrangeira vinculada ao id ( chave primária ) da hr_contact
tabela.
Associar um contato a um funcionário
Para interagir com os modelos Employee
e Contact
, você executa o shell_plus
comando com a --print-sql
opção:
python manage.py shell_plus --print-sql
Linguagem de código: Python ( python )
A --print-sql
opção gera o comando SQL que o Django executa.
Primeiro, crie um novo Employee
objeto e salve-o no banco de dados:
>>> e = Employee(first_name='John',last_name='Doe')
>>> e.save()
Linguagem de código: Python ( python )
Django executa o seguinte comando SQL:
INSERT INTO "hr_employee" ("first_name", "last_name", "contact_id")
VALUES ('John', 'Doe', NULL)
RETURNING "hr_employee"."id"
Linguagem de código: Python ( python )
Segundo, crie e salve um novo contato no banco de dados:
>>> c = Contact(phone='40812345678', address='101 N 1st Street, San Jose, CA')
>>> c.save()
Linguagem de código: Python ( python )
Django também executa o seguinte INSERT
comando:
INSERT INTO "hr_contact" ("phone", "address")
VALUES ('40812345678', '101 N 1st Street, San Jose, CA')
RETURNING "hr_contact"."id"
Linguagem de código: Python ( python )
Terceiro, associe um contato a um funcionário:
>>> e.contact = c
>>> e.save()
Linguagem de código: Python ( python )
Django atualiza o valor da contact_id
coluna da hr_employee
tabela para o valor da id
coluna da hr_contact
tabela.
UPDATE "hr_employee"
SET "first_name" = 'John',
"last_name" = 'Doe',
"contact_id" = 1
WHERE "hr_employee"."id" = 3
Linguagem de código: Python ( python )
Obtendo dados de um relacionamento um-para-um
Primeiro, pegue o funcionário com o nome John Doe
:
>>> e = Employee.objects.filter(first_name='John',last_name='Doe').first()
<Employee: John Doe>
Linguagem de código: Python ( python )
Django executa a SELECT
instrução que obtém a linha com first_name
'John'
and last_name
'Doe'
.
Podem hr_employee
ter vários funcionários com o mesmo nome e sobrenome. Portanto, o filter()
retorno a QuerySet
.
Para obter a primeira linha do QuerySet
, usamos o first()
método. O first()
método retorna uma única instância da Employee
classe.
SELECT "hr_employee"."id",
"hr_employee"."first_name",
"hr_employee"."last_name",
"hr_employee"."contact_id"
FROM "hr_employee"
WHERE ("hr_employee"."first_name" = 'John' AND "hr_employee"."last_name" = 'Doe')
ORDER BY "hr_employee"."id" ASC
LIMIT 1
Linguagem de código: Python ( python )
Observe que a consulta não obtém os dados de contato da hr_contact
tabela. Ele apenas obtém dados da hr_employee
tabela.
Ao acessar o contact
atributo do funcionário:
>>> e.contact
Linguagem de código: Python ( python )
… Django executa a segunda SELECT
instrução para obter dados da hr_contact
tabela:
SELECT "hr_contact"."id",
"hr_contact"."phone",
"hr_contact"."address"
FROM "hr_contact"
WHERE "hr_contact"."id" = 1
LIMIT 21
<Contact: 40812345678>
Linguagem de código: Python ( python )
O seguinte obtém o contato com id 1:
>>> c = Contact.objects.get(id=1)
Linguagem de código: Python ( python )
Ao associar um contato a um funcionário, você pode acessar a employee
partir do contact
objeto:
>>> c.employee
<Employee: John Doe>
Linguagem de código: Python ( python )
Django executa uma SELECT
instrução para obter dados da hr_employee
tabela:
SELECT "hr_employee"."id",
"hr_employee"."first_name",
"hr_employee"."last_name",
"hr_employee"."contact_id"
FROM "hr_employee"
WHERE "hr_employee"."contact_id" = 1
LIMIT 21
Linguagem de código: Python ( python )
Observe que a Contact
classe não possui o employee
atributo. No entanto, você pode acessar employee
se o contato estiver associado a um.
Vamos criar outro contato que não esteja associado a nenhum funcionário:
>>> c = Contact(phone='4081111111',address='202 N 1st Street, San Jose, CA')
>>> c.save()
Linguagem de código: Python ( python )
Django executará a seguinte INSERT
instrução:
INSERT INTO "hr_contact" ("phone", "address")
VALUES ('4081111111', '202 N 1st Street, San Jose, CA')
RETURNING "hr_contact"."id"
Linguagem de código: Python ( python )
Se você encontrar um contato que não esteja associado a nenhum funcionário e acessar o funcionário, você receberá uma RelatedObjectDoesNotExist
exceção.
Selecionando objetos relacionados
Primeiro, crie um novo funcionário:
>>> e = Employee(first_name='Jane',last_name='Doe')
>>> e.save()
INSERT INTO "hr_employee" ("first_name", "last_name", "contact_id")
VALUES ('Jane', 'Doe', NULL) RETURNING "hr_employee"."id"
Execution time: 0.003079s [Database: default]
Linguagem de código: Python ( python )
Em segundo lugar, faça com que todos os funcionários:
>>> Employee.objects.all()
Linguagem de código: Python ( python )
Django retorna dois funcionários:
<QuerySet [<Employee: John Doe>, <Employee: Jane Doe>]>
Linguagem de código: Python ( python )
Se você precisar exibir todos os funcionários e seus contatos na mesma página, terá o problema de consulta N+1:
- Primeiro, você precisa de uma consulta para obter todos os funcionários (N funcionários).
- Segundo, você precisa de N consultas para selecionar o contato relacionado de cada funcionário.
Para evitar isso, você pode consultar todos os funcionários e contatos usando uma única consulta usando o select_related()
método:
>>> Employee.objects.select_related('contact').all()
Linguagem de código: Python ( python )
Neste caso, o Django executa a seguinte LEFT
JOIN
instrução:
SELECT "hr_employee"."id",
"hr_employee"."first_name",
"hr_employee"."last_name",
"hr_employee"."contact_id",
"hr_contact"."id",
"hr_contact"."phone",
"hr_contact"."address"
FROM "hr_employee"
LEFT OUTER JOIN "hr_contact"
ON ("hr_employee"."contact_id" = "hr_contact"."id")
LIMIT 21
Linguagem de código: Python ( python )
O Django usa LEFT
JOIN
que retorna todos os funcionários da hr_employee
tabela e contatos associados aos funcionários selecionados:
<QuerySet [<Employee: John Doe>, <Employee: Jane Doe>]>
Linguagem de código: Python ( python )
Baixe o código fonte do projeto aqui
Resumo
- Use
OneToOneField
a classe para estabelecer um relacionamento um-para-um.