Qual é a diferença entre django OneToOneField e ForeignKey?

402

Qual é a diferença entre Django OneToOneFielde ForeignKey?

redice
fonte

Respostas:

507

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 ForeignKeycom unique=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.

Exemplo

Por exemplo, se tivermos os dois modelos a seguir (código completo do modelo abaixo):

  1. Car modelo usa OneToOneField(Engine)
  2. Car2 modelo usa ForeignKey(Engine2, unique=True)

De dentro, python manage.py shellexecute 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>

ForeignKeycom unique=Trueexemplo

>>> from testapp.models import Car2, Engine2
>>> c2 = Car2.objects.get(name='Mazda')
>>> e2 = Engine2.objects.get(name='Wankel')
>>> e2.car2_set.all()
[<Car2: Mazda>]

Código de modelo

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
Matthew Rankin
fonte
5
@ MarkPNeyer: tanto quanto eu entendo, um campo OneToOne é apenas isso: um para um. Não precisa ser usado. Veja este exemplo : um local não precisa ser um restaurante.
Osa
21
Esta resposta diz "existem algumas diferenças" e, em seguida, nomeia uma diferença. Existem outros?
Chris Martin
6
Estou me perguntando o mesmo que Chris. É simplesmente açúcar sintático, existe alguma diferença subjacente na maneira como os dados são acessados, levando a diferenças de desempenho?
Carlos
4
Existe uma razão fundamental pela qual o Django não pode ter uma regra que, se a chave estrangeira for única e não nula, e.cartambém funcione?
Ciro Santilli
4
Então ... quando alguém iria querer usar um ForeignKeycom unique=Truee não um OneToOneField? Vejo em outras perguntas que o Django até adverte que OneToOneFieldos interesses de quem geralmente serve melhor. O inverso QuerySetnunca terá mais de um elemento, certo?
26517 Andy
121

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.

Dan Breen
fonte
4
obrigado, Dose OneToOneField (someModel) significa ForeignKey (SomeModel, unique = True)?
redice
9
Sim: 'Um OneToOneField é essencialmente o mesmo que uma ForeignKey, com a exceção que sempre carrega uma restrição "única" e a relação inversa sempre retorna o objeto apontado (já que sempre haverá um), em vez de retornar um Lista.'
Dan Breen
11
E quanto a vários carros com o mesmo motor?
Oleg Belousov
3
@OlegTikhonov Eles podem ter uma cópia do mesmo design de motor, mas eu gostaria de ver uma instância em que vários carros estão compartilhando o mesmo motor físico.
quer
3
Há um pouco de confusão sobre os termos nesta resposta. O ForeignKey não é um para muitos, mas é um relacionamento para muitos, de acordo com a documentação oficial do django: docs.djangoproject.com/en/2.0/ref/models/fields/…
Kutay Demireren
45

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 syncdbpara executar o código sql e criar as tabelas para o seu aplicativo no seu banco de dados. Em seguida, use python manage.py shellpara 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 OneToOneFielde perceber a diferença. Tenho certeza de que, após este exemplo, você saberá completamente a diferença entre django OneToOneFielde django ForeignKey.

jetbird13
fonte
Eu gosto disso. A diferença fundamental entre o OneToOne e o ForeignKey é um para um e um para muitos. Você pode usar ForeignKey e unique = True para fazer um por um, a diferença sutil é afirmada na resposta de Matthew.
FrankZhu
13

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.

andrers52
fonte
3
Boa analogia, mas nem sempre é assim. Existem alguns casos extremos que não se encaixam nessa explicação. Digamos, por exemplo, que temos classes Patiente Organ. Patientpode ter muitos Organs, mas um Organpode pertencer a apenas um Patient. Quando Patienté excluído, todos os Organs também são excluídos. Eles não podem existir sem a Patient.
Cezar
4

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 OneToOneFieldcomo chave primária (imagine o UserProfilemodelo por exemplo):

user = models.OneToOneField(
    User, null=False, primary_key=True, verbose_name='Member profile')
Dmitriy Sintsov
fonte
3

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.

Sim.
fonte
1

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.

aarif faridi
fonte
1

ForeignKey permite que você receba subclasses. É a definição de outra classe, mas o OneToOneFields não pode fazer isso e não é acoplável a várias variáveis.

Bitdom8
fonte