¿Cuál es la diferencia entre django OneToOneField y ForeignKey?


Respuestas:


507

Tenga cuidado de darse cuenta de que hay algunas diferencias entre OneToOneField(SomeModel)y ForeignKey(SomeModel, unique=True). Como se indica en la Guía definitiva de Django :

OneToOneField

Una relación uno a uno. Conceptualmente, esto es similar a un ForeignKeycon unique=True, pero el lado "inverso" de la relación devolverá directamente un solo objeto.

En contraste con la relación OneToOneField"inversa", una relación ForeignKey"inversa" devuelve a QuerySet.

Ejemplo

Por ejemplo, si tenemos los dos modelos siguientes (código de modelo completo a continuación):

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

Desde adentro python manage.py shellejecute lo siguiente:

OneToOneField Ejemplo

>>> from testapp.models import Car, Engine
>>> c = Car.objects.get(name='Audi')
>>> e = Engine.objects.get(name='Diesel')
>>> e.car
<Car: Audi>

ForeignKeycon el unique=Trueejemplo

>>> 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 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

55
@ MarkPNeyer: por lo que yo entiendo, un campo OneToOne es solo eso: uno a uno. No tiene que estar en. Vea este ejemplo : un lugar no tiene que ser un restaurante.
osa

21
Esta respuesta dice "hay algunas diferencias", y luego nombra una diferencia. ¿Hay otros?
Chris Martin

66
Me pregunto lo mismo que Chris. ¿Es simplemente azúcar sintáctica, hay alguna diferencia subyacente en cómo se accede a los datos, lo que lleva a diferencias de rendimiento?
Carlos

44
¿Hay una razón fundamental por la que Django no podría tener una regla tal que si la clave externa es única y no nula, entonces e.cartambién funciona?
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

44
Entonces ... ¿cuándo querría uno usar un ForeignKeycon en unique=Truelugar de un OneToOneField? Veo en otras preguntas que Django incluso advierte que OneToOneFieldlos intereses de un mejor servicio suelen ser los mejores. Lo contrario QuerySetnunca tendrá más de un elemento, ¿verdad?
Andy

121

Una ForeignKey es para uno a muchos, por lo que un objeto Car puede tener muchas Ruedas, cada Rueda tiene una ForeignKey para el Car al que pertenece. Un OneToOneField sería como un motor, donde un objeto Car puede tener uno y solo uno.


44
gracias, Dose OneToOneField (someModel) significa ForeignKey (SomeModel, unique = True)?
redice

99
Sí: 'OneToOneField es esencialmente lo mismo que ForeignKey, con la excepción de que siempre conlleva una restricción "única" y la relación inversa siempre devuelve el objeto señalado (ya que solo habrá uno), en lugar de devolver un lista.'
Dan Breen,

1
¿Qué pasa con varios autos que tienen el mismo motor?
Oleg Belousov

3
@OlegTikhonov Es posible que tengan una copia del mismo diseño de motor, pero me gustaría ver una instancia en la que varios autos compartan el mismo motor físico.
Dan Breen

3
Hay una pequeña confusión sobre los términos en esta respuesta. ForeignKey no es uno a muchos, pero es una relación de muchos a uno según la documentación oficial de django: docs.djangoproject.com/en/2.0/ref/models/fields/…
Kutay Demireren

45

La mejor y más efectiva forma de aprender cosas nuevas es ver y estudiar ejemplos prácticos del mundo real. Supongamos por un momento que desea crear un blog en django donde los reporteros puedan escribir y publicar artículos de noticias. El propietario del periódico en línea quiere permitir que cada uno de sus reporteros publique tantos artículos como quiera, pero no quiere que diferentes reporteros trabajen en el mismo artículo. Esto significa que cuando los lectores van y leen un artículo, verán solo un autor en el artículo.

Por ejemplo: Artículo de John, Artículo de Harry, Artículo de Rick. No puede tener el artículo de Harry & Rick porque el jefe no quiere que dos o más autores trabajen en el mismo artículo.

¿Cómo podemos resolver este 'problema' con la ayuda de django? La clave para la solución de este problema es el django.ForeignKey .

El siguiente es el código completo que se puede utilizar para implementar la idea de nuestro jefe.

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

Ejecute python manage.py syncdbpara ejecutar el código sql y compile las tablas para su aplicación en su base de datos. Luego, use python manage.py shellpara abrir un shell de python.

Cree el objeto Reporter R1.

In [49]: from thepub.models import Reporter, Article

In [50]: R1 = Reporter(first_name='Rick')

In [51]: R1.save()

Cree el objeto Artículo A1.

In [5]: A1 = Article.objects.create(title='TDD In Django', reporter=R1)

In [6]: A1.save()

Luego use el siguiente código para obtener el nombre del reportero.

In [8]: A1.reporter.first_name
Out[8]: 'Rick'

Ahora cree el objeto Reporter R2 ejecutando el siguiente código de Python.

In [9]: R2 = Reporter.objects.create(first_name='Harry')

In [10]: R2.save()

Ahora intente agregar R2 al objeto Artículo A1.

In [13]: A1.reporter.add(R2)

No funciona y obtendrá un AttributeError que dice que el objeto 'Reportero' no tiene el atributo 'agregar'.

Como puede ver, un objeto Artículo no puede estar relacionado con más de un objeto Reportero.

¿Qué hay de R1? ¿Podemos adjuntarle más de un objeto de artículo?

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 ejemplo práctico nos muestra que django ForeignKeyse usa para definir relaciones de muchos a uno.

OneToOneField se usa para crear relaciones uno a uno.

Nosotros podemos usar reporter = models.OneToOneField(Reporter) en el archivo models.py anterior, pero no será útil en nuestro ejemplo, ya que un autor no podrá publicar más de un artículo.

Cada vez que desee publicar un nuevo artículo, tendrá que crear un nuevo objeto Reporter. Esto lleva mucho tiempo, ¿no?

Recomiendo probar el ejemplo con el OneToOneFieldy darse cuenta de la diferencia. Estoy bastante seguro de que después de este ejemplo sabrás por completo la diferencia entre django OneToOneFieldy django ForeignKey.


Me gusta esto. La diferencia fundamental entre OneToOne y ForeignKey es una relación uno a uno y uno a muchos. Puede usar ForeignKey y unique = True para hacer uno a uno, la diferencia sutil se indica en la respuesta de Matthew.
FrankZhu

13

OneToOneField (uno a uno) se da cuenta, en orientación de objeto, la noción de composición, mientras ForeignKey (uno a muchos) se relaciona con la agregación.


3
Buena analogía, pero no siempre es así. Hay algunos casos extremos que no encajan en esta explicación. Digamos por ejemplo que tenemos clases Patienty Organ. Patientpuede tener muchos Organs, pero un solo Organpuede pertenecer a uno Patient. Cuando Patientse elimina, todos los Organcorreos electrónicos también se eliminan. No pueden existir sin un Patient.
cezar

4

También OneToOneFieldes útil para usarse como clave principal para evitar la duplicación de claves. Uno puede no tener autocampo implícito / explícito

models.AutoField(primary_key=True)

pero use OneToOneFieldcomo clave primaria en su lugar (imagine el UserProfilemodelo, por ejemplo):

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

3

Cuando accede a OneToOneField obtiene el valor del campo que ha consultado. En este ejemplo, el campo 'título' de un modelo de libro es OneToOneField:

>>> from mysite.books.models import Book
>>> b = Book.objects.get(id=50)
>>> b.title
u'The Django Book'

Cuando accede a una ForeignKey, obtiene el objeto de modelo relacionado, que luego puede realizar más consultas. En este ejemplo, el campo 'editor' del mismo modelo de libro es una ForeignKey (correlacionada con la definición del modelo de clase Publisher):

>>> b = Book.objects.get(id=50)
>>> b.publisher
<Publisher: Apress Publishing>
>>> b.publisher.website
u'http://www.apress.com/'

Con los campos ForeignKey, las consultas también funcionan a la inversa, pero son ligeramente diferentes debido a la naturaleza no simétrica de la relación.

>>> p = Publisher.objects.get(name='Apress Publishing')
>>> p.book_set.all()
[<Book: The Django Book>, <Book: Dive Into Python>, ...]

Detrás de escena, book_set es solo un QuerySet y se puede filtrar y dividir como cualquier otro QuerySet. El nombre del atributo book_set se genera agregando el nombre del modelo en minúsculas a _set.


1

OneToOneField: si la segunda tabla está relacionada con

table2_col1 = models.OneToOneField(table1,on_delete=models.CASCADE, related_name='table1_id')

table2 contendrá solo un registro correspondiente al valor pk de table1, es decir, table2_col1 tendrá un valor único igual a pk de table

table2_col1 == models.ForeignKey(table1, on_delete=models.CASCADE, related_name='table1_id')

table2 puede contener más de un registro correspondiente al valor pk de table1.


1

ForeignKey le permite recibir subclases si es una definición de otra clase, pero OneToOneFields no puede hacer esto y no se puede adjuntar a múltiples variables

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.