Respostas:
Tenha cuidado para perceber que existem algumas diferenças entre OneToOneField(SomeModel)
e ForeignKey(SomeModel, unique=True)
. Conforme declarado no Guia Definitivo do Django :
OneToOneField
Um relacionamento individual. Conceitualmente, isso é semelhante a um
ForeignKey
comunique=True
, mas o lado "reverso" da relação retornará diretamente um único objeto.
Em contraste com a relação OneToOneField
"reversa", uma relação ForeignKey
"reversa" retorna a QuerySet
.
Por exemplo, se tivermos os dois modelos a seguir (código completo do modelo abaixo):
Car
modelo usa OneToOneField(Engine)
Car2
modelo usa ForeignKey(Engine2, unique=True)
De dentro, python manage.py shell
execute o seguinte:
OneToOneField
Exemplo>>> from testapp.models import Car, Engine
>>> c = Car.objects.get(name='Audi')
>>> e = Engine.objects.get(name='Diesel')
>>> e.car
<Car: Audi>
ForeignKey
com unique=True
exemplo>>> from testapp.models import Car2, Engine2
>>> c2 = Car2.objects.get(name='Mazda')
>>> e2 = Engine2.objects.get(name='Wankel')
>>> e2.car2_set.all()
[<Car2: Mazda>]
from django.db import models
class Engine(models.Model):
name = models.CharField(max_length=25)
def __unicode__(self):
return self.name
class Car(models.Model):
name = models.CharField(max_length=25)
engine = models.OneToOneField(Engine)
def __unicode__(self):
return self.name
class Engine2(models.Model):
name = models.CharField(max_length=25)
def __unicode__(self):
return self.name
class Car2(models.Model):
name = models.CharField(max_length=25)
engine = models.ForeignKey(Engine2, unique=True, on_delete=models.CASCADE)
def __unicode__(self):
return self.name
e.car
também funcione?
ForeignKey
com unique=True
e não um OneToOneField
? Vejo em outras perguntas que o Django até adverte que OneToOneField
os interesses de quem geralmente serve melhor. O inverso QuerySet
nunca terá mais de um elemento, certo?
Uma ForeignKey é para um para muitos, portanto, um objeto Car pode ter muitas Rodas, cada Roda tendo uma ForeignKey no Carro ao qual pertence. Um OneToOneField seria como um mecanismo, onde um objeto Car pode ter um e apenas um.
A melhor e a maneira mais eficaz de aprender coisas novas é ver e estudar exemplos práticos do mundo real. Suponha por um momento que você queira criar um blog no django onde os repórteres possam escrever e publicar artigos de notícias. O proprietário do jornal on-line quer permitir que cada um de seus repórteres publique quantos artigos quiser, mas não quer que repórteres diferentes trabalhem no mesmo artigo. Isso significa que, quando os leitores lerem um artigo, verão apenas um autor no artigo.
Por exemplo: Artigo de John, Artigo de Harry, Artigo de Rick. Você não pode ter o artigo de Harry & Rick porque o chefe não deseja que dois ou mais autores trabalhem no mesmo artigo.
Como podemos resolver esse 'problema' com a ajuda do django? A chave para a solução desse problema é o django ForeignKey
.
A seguir, está o código completo que pode ser usado para implementar a ideia do nosso chefe.
from django.db import models
# Create your models here.
class Reporter(models.Model):
first_name = models.CharField(max_length=30)
def __unicode__(self):
return self.first_name
class Article(models.Model):
title = models.CharField(max_length=100)
reporter = models.ForeignKey(Reporter)
def __unicode__(self):
return self.title
Execute python manage.py syncdb
para executar o código sql e criar as tabelas para o seu aplicativo no seu banco de dados. Em seguida, use python manage.py shell
para abrir um shell python.
Crie o objeto Reporter R1.
In [49]: from thepub.models import Reporter, Article
In [50]: R1 = Reporter(first_name='Rick')
In [51]: R1.save()
Crie o objeto de artigo A1.
In [5]: A1 = Article.objects.create(title='TDD In Django', reporter=R1)
In [6]: A1.save()
Em seguida, use o seguinte trecho de código para obter o nome do repórter.
In [8]: A1.reporter.first_name
Out[8]: 'Rick'
Agora crie o objeto Reporter R2 executando o seguinte código python.
In [9]: R2 = Reporter.objects.create(first_name='Harry')
In [10]: R2.save()
Agora tente adicionar R2 ao objeto Artigo A1.
In [13]: A1.reporter.add(R2)
Ele não funciona e você receberá um AttributeError dizendo que o objeto 'Reporter' não tem atributo 'add'.
Como você pode ver, um objeto Artigo não pode estar relacionado a mais de um objeto Reporter.
E o R1? Podemos anexar mais de um objeto de artigo a ele?
In [14]: A2 = Article.objects.create(title='Python News', reporter=R1)
In [15]: R1.article_set.all()
Out[15]: [<Article: Python News>, <Article: TDD In Django>]
Este exemplo prático nos mostra que o django ForeignKey
é usado para definir relacionamentos muitos-para-um.
OneToOneField
é usado para criar relacionamentos um a um.
Podemos usar reporter = models.OneToOneField(Reporter)
o arquivo models.py acima, mas não será útil em nosso exemplo, pois um autor não poderá postar mais de um artigo.
Sempre que você desejar publicar um novo artigo, será necessário criar um novo objeto Reporter. Isso consome tempo, não é?
Eu recomendo tentar o exemplo com o OneToOneField
e perceber a diferença. Tenho certeza de que, após este exemplo, você saberá completamente a diferença entre django OneToOneField
e django ForeignKey
.
O OneToOneField (um para um) realiza, na orientação a objetos, a noção de composição, enquanto ForeignKey (um para muitos) se refere à agregação.
Patient
e Organ
. Patient
pode ter muitos Organ
s, mas um Organ
pode pertencer a apenas um Patient
. Quando Patient
é excluído, todos os Organ
s também são excluídos. Eles não podem existir sem a Patient
.
Também OneToOneField
é útil para ser usado como chave primária para evitar duplicação de chave. Pode-se não ter autofield implícito / explícito
models.AutoField(primary_key=True)
mas use OneToOneField
como chave primária (imagine o UserProfile
modelo por exemplo):
user = models.OneToOneField(
User, null=False, primary_key=True, verbose_name='Member profile')
Quando você acessa um OneToOneField, obtém o valor do campo consultado. Neste exemplo, o campo 'title' do modelo de livro é um OneToOneField:
>>> from mysite.books.models import Book
>>> b = Book.objects.get(id=50)
>>> b.title
u'The Django Book'
Ao acessar uma ForeignKey, você obtém o objeto de modelo relacionado, com o qual você pode realizar outras consultas. Neste exemplo, o campo 'publicador' do mesmo modelo de livro é uma ForeignKey (correlacionada à definição do modelo de classe do Publisher):
>>> b = Book.objects.get(id=50)
>>> b.publisher
<Publisher: Apress Publishing>
>>> b.publisher.website
u'http://www.apress.com/'
Com os campos ForeignKey, as consultas também funcionam de outra maneira, mas são ligeiramente diferentes devido à natureza não simétrica do relacionamento.
>>> p = Publisher.objects.get(name='Apress Publishing')
>>> p.book_set.all()
[<Book: The Django Book>, <Book: Dive Into Python>, ...]
Nos bastidores, book_set é apenas um QuerySet e pode ser filtrado e fatiado como qualquer outro QuerySet. O nome do atributo book_set é gerado anexando o nome do modelo em minúsculas a _set.
OneToOneField: se a segunda tabela estiver relacionada com
table2_col1 = models.OneToOneField(table1,on_delete=models.CASCADE, related_name='table1_id')
table2 conterá apenas um registro correspondente ao valor pk da tabela1, ou seja, table2_col1 terá um valor único igual a pk da tabela
table2_col1 == models.ForeignKey(table1, on_delete=models.CASCADE, related_name='table1_id')
A tabela2 pode conter mais de um registro correspondente ao valor de pk da tabela1.