Relacionamento Um-para-Um (One-to-One) no Django
O relacionamento Um-para-Um ocorre quando um registro de uma entidade pode estar associado a, no máximo, um registro de outra entidade. No Django, esse tipo de relação é representado pelo campo OneToOneField
.
Um exemplo prático seria separar os dados de um usuário entre duas models:
- A model
User
armazena informações básicas, como nome. - A model
Account
armazena dados de autenticação, como login e senha.
from django.db import models
class User(models.Model):
name = models.CharField(max_length=20)
class Account(models.Model):
login = models.CharField(max_length=20)
password = models.CharField(max_length=20)
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
O campo OneToOneField
estabelece a relação um-para-um entre Account
e User
. O primeiro argumento especifica a model associada, que neste caso é User
. O parâmetro on_delete=models.CASCADE
garante que, ao remover um usuário, o registro correspondente em Account
também seja excluído. Além disso, primary_key=True
transforma o campo user na chave primária da model Account
, eliminando a necessidade de um campo id
separado.
Após a migração, um banco SQLite criaria as seguintes tabelas:
CREATE TABLE "hello_user" (
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"name" varchar(20) NOT NULL
);
CREATE TABLE "hello_account" (
"login" varchar(20) NOT NULL,
"password" varchar(20) NOT NULL,
"user_id" bigint NOT NULL PRIMARY KEY REFERENCES "hello_user" ("id") DEFERRABLE INITIALLY DEFERRED
);
O campo user_id
na tabela hello_account
atua como chave primária e estrangeira ao mesmo tempo, garantindo que cada Account
esteja vinculada a um único User
.
Operações com Models
A model Account
possui um campo user
, permitindo acessar e modificar o usuário associado.
# Criando um usuário
sam = User.objects.create(name="Sam")
# Criando uma conta para o usuário
acc = Account.objects.create(login="1234", password="6565", user=sam)
# Atualizando o nome do usuário
acc.user.name = "Bob"
acc.user.save()
print(f"{acc.user.name}, login: {acc.login}, password: {acc.password}")
# Saída: Bob, login: 1234, password: 6565
A partir da model Account
, é possível acessar diretamente o usuário associado e modificar seus dados.
Mesmo que a model User
não tenha um campo explícito apontando para Account
, o Django cria automaticamente um related manager, permitindo acessar a conta associada pelo nome da model dependente (account
, neste caso):
# Criando um usuário
tom = User.objects.create(name="Tom")
# Criando uma conta e associando-a ao usuário
acc = Account(login="1234", password="6565")
tom.account = acc
tom.account.save()
# Atualizando os dados da conta
tom.account.login = "qwerty"
tom.account.password = "123456"
tom.account.save()
print(f"{tom.name}, login: {tom.account.login}, password: {tom.account.password}")
# Saída: Tom, login: qwerty, password: 123456
O Django cria automaticamente o atributo account na model User
, permitindo acessar diretamente os dados da model Account
.
Filtro de Dados
A relação um-para-um permite realizar consultas baseadas nos atributos das duas models:
# Obtendo um usuário
tom = User.objects.get(name="Tom")
# Obtendo a conta associada ao usuário
tom_acc = Account.objects.get(user=tom)
print(f"login: {tom_acc.login}, password: {tom_acc.password}")
# Saída: login: qwerty, password: 123456
# Obtendo uma conta pelo nome do usuário
bob_acc = Account.objects.get(user__name="Bob")
print(f"login: {bob_acc.login}, password: {bob_acc.password}")
# Obtendo um usuário a partir do login da conta
user = User.objects.get(account__login="qwerty")
print(user.name)
A sintaxe campo__atributo
(com dois sublinhados) permite acessar atributos de models relacionadas no filtro.
Documentação oficial: