relations - ¿Cuál es la diferencia entre django OneToOneField y ForeignKey?
one to one relations django (6)
¿Cuál es la diferencia entre django OneToOneField y ForeignKey?
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 un OneToOneField:
>>> from mysite.books.models import Book
>>> b = Book.objects.get(id=50)
>>> b.title
u''The Django Book''
Cuando accede a ForeignKey, obtiene el objeto de modelo relacionado, con el que puede realizar más consultas. En este ejemplo, el mismo campo de "editor" del modelo de libro es un ForeignKey (que se correlaciona con la definición del modelo de clase de Publisher)
>>> b = Book.objects.get(id=50)
>>> b.publisher
<Publisher: Apress Publishing>
>>> b.publisher.website
u''http://www.apress.com/''
Con ForeignKey, las consultas de campo también funcionan de la otra manera, 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 de atributo book_set se genera agregando el nombre del modelo en minúsculas a _set.
La mejor y más efectiva manera 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 quieran, pero no quiere que diferentes reporteros trabajen en el mismo artículo. Esto significa que cuando los lectores van y leen un artículo solo verán un autor en el artículo.
Por ejemplo: Artículo de John, Artículo de Harry, Artículo de Rick. No puedes tener un 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 puede usarse 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 syncdb
para ejecutar el código sql y python manage.py syncdb
las tablas para su aplicación en su base de datos. Luego usa el python manage.py shell
para abrir un shell de python.
Crea el objeto Reporter R1.
In [49]: from thepub.models import Reporter, Article
In [50]: R1 = Reporter(first_name=''Rick'')
In [51]: R1.save()
Crear el objeto del 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 trate de agregar R2 al objeto del artículo A1.
In [13]: A1.reporter.add(R2)
No funciona y obtendrá un AttributeError que dice que el objeto ''Reporter'' no tiene ningún atributo ''add''.
Como puede ver, un objeto Artículo no puede estar relacionado con más de un objeto Reportero.
¿Qué pasa con R1? ¿Podemos adjuntarle más de un 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 ForeignKey
se utiliza para definir relaciones de varios a uno.
OneToOneField
se utiliza para crear relaciones de uno a uno.
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 consume mucho tiempo, ¿no es así?
Recomiendo probar el ejemplo con OneToOneField
y darse cuenta de la diferencia. Estoy bastante seguro de que después de este ejemplo sabrá completamente la diferencia entre django OneToOneField
y django ForeignKey
.
OneToOneField (one-to-one) realiza, en la orientación a objetos, la noción de composición, mientras que ForeignKey (one-to-many) se relaciona con la agregación.
También es útil usar OneToOneField
como clave principal para evitar la duplicación de claves. Uno no puede tener autocampo implícito / explícito
models.AutoField(primary_key=True)
pero use OneToOneField
como clave principal (por ejemplo, imagine el modelo UserProfile
):
user = models.OneToOneField(
User, null=False, primary_key=True, verbose_name=''Member profile'')
Una ForeignKey es para uno a muchos, por lo que un objeto Car puede tener muchas Ruedas, y cada Rueda tiene una ForeignKey para el Coche al que pertenece. Un OneToOneField sería como un Motor, donde un objeto de Coche puede tener uno y solo uno.
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 de uno a uno. Conceptualmente, esto es similar a un
ForeignKey
conunique=True
, pero el lado "inverso" de la relación devolverá directamente un solo objeto.
A diferencia de la OneToOneField
"inversa" de OneToOneField
, una relación "inversa" de ForeignKey
devuelve un QuerySet
.
Ejemplo
Por ejemplo, si tenemos los siguientes dos modelos (código de modelo completo a continuación):
- Modelo de
Car
utilizaOneToOneField(Engine)
-
Car2
modeloCar2
usaForeignKey(Engine2, unique=True)
Desde dentro de python manage.py shell
ejecute lo siguiente:
Ejemplo de OneToOneField
>>> from testapp.models import Car, Engine
>>> c = Car.objects.get(name=''Audi'')
>>> e = Engine.objects.get(name=''Diesel'')
>>> e.car
<Car: Audi>
ForeignKey
with unique=True
Ejemplo
>>> 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