Django ORM – Ejemplo de múltiples tablas
Las relaciones entre tablas se pueden dividir en los siguientes tres tipos:
- Uno a uno: una persona corresponde a un número de DNI, el campo de datos se establece como unique.
- Uno a muchos: una familia tiene varias personas, generalmente se implementa mediante una clave externa.
- Muchos a muchos: un estudiante tiene varios cursos, un curso tiene muchos estudiantes, generalmente se implementa la relación mediante una tercera tabla.

Crear modelos
A continuación veamos ejemplos de múltiples tablas.
Ejemplo
title = models.CharField(max_length=32)
price = models.DecimalField(max_digits=5, decimal_places=2)
pub_date = models.DateField()
publish = models.ForeignKey("Publish", on_delete=models.CASCADE)
authors = models.ManyToManyField("Author")
class Publish(models.Model):
name = models.CharField(max_length=32)
city = models.CharField(max_length=64)
email = models.EmailField()
class Author(models.Model):
name = models.CharField(max_length=32)
age = models.SmallIntegerField()
au_detail = models.OneToOneField("AuthorDetail", on_delete=models.CASCADE)
class AuthorDetail(models.Model):
gender_choices = (
(0, "Mujer"),
(1, "Hombre"),
(2, "Confidencial"),
)
gender = models.SmallIntegerField(choices=gender_choices)
tel = models.CharField(max_length=32)
addr = models.CharField(max_length=64)
birthday = models.DateField()
Explicación:
- 1. El tipo de dato EmailField tiene formato de correo electrónico, hereda internamente de CharField, está encapsulado, equivalente a varchar en MySQL.
- 2. La versión Django 1.1 no requiere borrado en cascada: on_delete=models.CASCADE, Django 2.2 sí lo requiere.
- 3. Generalmente no es necesario configurar la actualización en cascada.
- 4. La clave externa se configura en el lado "muchos" de la relación uno a muchos:models.ForeignKey("Nombre de la clase relacionada", on_delete=models.CASCADE)。
- 5. OneToOneField = ForeignKey(..., unique=True) establece la relación uno a uno.
- 6. Si una clase de modelo tiene una clave externa, al crear datos primero se deben crear los datos de la clase de modelo asociada a la clave externa; de lo contrario, al crear datos de la clase de modelo que contiene la clave externa, no se encontrarán los datos de la clase de modelo asociada a la clave externa.
Estructura de tabla
Tabla de libros Book: title, price, pub_date, publish (clave externa, muchos a uno), authors (muchos a muchos)
Tabla de editoriales Publish:name 、 city 、 email
Tabla de autores Author: name, age, au_detail (uno a uno)
Tabla de detalles del autor AuthorDetail:gender 、 tel 、 addr 、 birthday
La siguiente es la descripción de las relaciones de las tablas:
Insertar datos
Ejecutamos las siguientes operaciones de inserción SQL en MySQL:
insert into app01_publish(name,city,email) values ("华山出版社", "华山", "[email protected]"), ("明教出版社", "黑木崖", "[email protected]")
# 先插入 authordetail 表中多数据
insert into app01_authordetail(gender,tel,addr,birthday) values (1,13432335433,"华山","1994-5-23"), (1,13943454554,"黑木崖","1961-8-13"), (0,13878934322,"黑木崖","1996-5-20")
# 再将数据插入 author,这样 author 才能找到 authordetail
insert into app01_author(name,age,au_detail_id) values ("令狐冲",25,1), ("任我行",58,2), ("任盈盈",23,3)

ORM - Agregar datos
Uno a muchos (clave externa ForeignKey)
Método 1:En forma de pasar el objeto, el tipo de dato del valor de retorno es un objeto, el objeto libro.
Pasos:
- a. Obtener el objeto editorial
- b. Asignar el objeto editorial al atributo pulish del libro.
Código del archivo app01/views.py:
# Obtener el objeto editorial
pub_obj = models.Publish.objects.filter(pk=1).first()
# Asignar el objeto editorial al atributo publish del libro
book = models.Book.objects.create(title="Tutorial para novatos", price=200, pub_date="2010-10-10", publish=pub_obj)
print(book, type(book))
return HttpResponse(book)

Método 2: pasar el id del objeto (como los datos recibidos generalmente son id, pasar el id del objeto es lo común).
En una relación uno a muchos, en la clase (tabla del lado "muchos") que configura el atributo de clave externa, el nombre del campo que se muestra en MySQL es:nombre_atributo_clave_externa_id。
El tipo de dato del valor de retorno es un objeto, el objeto libro.
Pasos:
- a. Obtener el id del objeto editorial
- b. Asignar el id del objeto editorial al campo de editorial relacionada del libro pulish_id
Código del archivo app01/views.py:
# Obtener el objeto editorial
pub_obj = models.Publish.objects.filter(pk=1).first()
# Obtener el id del objeto editorial
pk = pub_obj.pk
# Asignar el id del objeto editorial al campo de editorial relacionada del libro publish_id
book = models.Book.objects.create(title="Técnica de espada Chong Ling", price=100, pub_date="2004-04-04", publish_id=pk)
print(book, type(book))
return HttpResponse(book)

Muchos a muchos (ManyToManyField): agregar datos en la tercera tabla de relación
Método 1:En forma de pasar el objeto, sin valor de retorno.
Pasos:
- a. Obtener el objeto autor
- b. Obtener el objeto libro
- c. Al atributo authors del objeto libro, pasar el objeto autor usando el método add
Código del archivo app01/views.py:
# Obtener el objeto autor
chong = models.Author.objects.filter(name="Linghu Chong").first()
ying = models.Author.objects.filter(name="Ren Yingying").first()
# Obtener el objeto libro
book = models.Book.objects.filter(title="Tutorial para novatos").first()
# Al atributo authors del objeto libro, pasar el objeto autor usando el método add
book.authors.add(chong, ying)
return HttpResponse(book)

Método 2:En forma de pasar el id del objeto, sin valor de retorno.
Pasos:
- a. Obtener el id del objeto autor
- b. Obtener el objeto libro
- c. Al atributo authors del objeto libro, pasar el id del objeto autor usando el método add
Código del archivo app01/views.py:
# Obtener el objeto autor
chong = models.Author.objects.filter(name="Linghu Chong").first()
# Obtener el id del objeto autor
pk = chong.pk
# Obtener el objeto libro
book = models.Book.objects.filter(title="Técnica de espada Chong Ling").first()
# Al atributo authors del objeto libro, pasar el id del objeto autor usando el método add
book.authors.add(pk)

Gestor de relaciones (llamada de objeto)
Requisitos previos:
- Muchos a muchos (ambos lados tienen gestor de relaciones)
- Uno a muchos (solo el objeto de la clase del lado "muchos" tiene gestor de relaciones, es decir, solo en la dirección inversa)
Formato de sintaxis:
正向:属性名 反向:小写类名加 _set
Nota:En uno a muchos solo se puede en dirección inversa
Métodos comunes:
add(): se usa para muchos a muchos, agrega el objeto de modelo especificado al conjunto de objetos relacionados (tabla de relación).
Nota:add() en uno a muchos (es decir, clave externa) solo puede pasar objetos (tipo de dato *QuerySet), no puede pasar ids (*[lista de ids]).
*[ ]Uso de :
# Método 1: pasar objetoauthor_list = models.Author.objects.filter(id__gt=2)
book_obj.authors.add(*author_list) # Agregar los objetos autor con id mayor que 2 al conjunto de autores de este libro
# Método 2: pasar objeto id
book_obj.authors.add(*[1,3]) # Agregar los objetos autor con id=1 e id=3 al conjunto de autores de este libro
return HttpResponse("ok")

Inversa:nombre_tabla_en_minúsculas_set
book = models.Book.objects.filter(title="Técnica de espada Chong Ling").first()
ying.book_set.add(book)
return HttpResponse("ok")

create(): crea un nuevo objeto y al mismo tiempo lo agrega al conjunto de objetos relacionados.
Devuelve el objeto recién creado.
wo = models.Author.objects.filter(name="Ren Woxing").first()
book = wo.book_set.create(title="Técnica de absorción de estrellas", price=300, pub_date="1999-9-19", publish=pub)
print(book, type(book))
return HttpResponse("ok")

remove(): elimina el objeto de modelo especificado del conjunto de objetos relacionados.
Para objetos ForeignKey, este método solo existe cuando null=True (puede ser nulo). No tiene valor de retorno.
Ejemplo
book_obj = models.Book.objects.get(id=11)
author_obj.book_set.remove(book_obj)
return HttpResponse("ok")

clear(): elimina todos los objetos del conjunto de objetos relacionados, elimina la relación, no elimina los objetos.
Para objetos ForeignKey, este método solo existe cuando null=True (puede ser nulo).
No tiene valor de retorno.
book = models.Book.objects.filter(title="Tutorial para novatos").first()
book.authors.clear()
Consulta ORM
Consulta entre tablas basada en objetos.
正向:属性名称 反向:小写类名_set
Uno a muchos
Consultar la ciudad donde se encuentra la editorial del libro con clave primaria 1 (dirección directa).
Ejemplo
res = book.publish.city
print(res, type(res))
return HttpResponse("ok")

Consultar los nombres de los libros publicados por la Editorial Mingjiao (inverso).
Inverso:objeto.nombre_clase_en_minúsculas_set(pub.book_set)Puede saltar a la tabla asociada (tabla de libros).
pub.book_set.all(): Obtener todos los objetos de libros de la tabla de libros, en un QuerySet, e iterar para obtener cada objeto de libro.
Ejemplo
res = pub.book_set.all()
for i in res:
print(i.title)
return HttpResponse("ok")

Uno a uno
Consultar el teléfono de Linghu Chong (directo)
Directo: objeto.atributo (author.au_detail) puede saltar a la tabla asociada (tabla de detalles del autor)
Ejemplo
res = author.au_detail.tel
print(res, type(res))
return HttpResponse("ok")

Consultar los nombres de todos los autores cuya dirección está en el Acantilado Heimu (inverso).
El inverso de uno a uno, se usaobjeto.nombre_clase_en_minúsculases suficiente, no hay que añadir _set.
Inverso: objeto.nombre_clase_en_minúsculas (addr.author) puede saltar a la tabla asociada (tabla de autores).
Ejemplo
res = addr.author.name
print(res, type(res))
return HttpResponse("ok")

Muchos a muchos
Nombres y números de teléfono de todos los autores del tutorial de Ejemplo (directo).
Directo:objeto.atributo (book.authors)Puede saltar a la tabla asociada (tabla de autores).
La tabla de autores no contiene el teléfono del autor, por lo que nuevamente se usaobjeto.atributo (i.au_detail)para saltar a la tabla asociada (tabla de detalles del autor).
Ejemplo
res = book.authors.all()
for i in res:
print(i.name, i.au_detail.tel)
return HttpResponse("ok")

Consultar los nombres de todos los libros publicados por Ren Woxing (inverso).
Ejemplo
res = author.book_set.all()
for i in res:
print(i.title)
return HttpResponse("ok")

Consulta entre tablas basada en doble guion bajo
Directa: nombre_atributo__nombre_atributo_de_la_tabla_cruzada Inversa: nombre_clase_en_minúsculas__nombre_atributo_de_la_tabla_cruzada
Uno a muchos
Consultar los nombres y precios de todos los libros publicados por la Editorial Ejemplo.
Ejemplo

Inverso: mediante el nombre de clase enminúsculas__nombre_atributo_de_otra_tabla (book__title, book__price)Obtener datos a través de las tablas.
Ejemplo
return HttpResponse("ok")

Muchos a muchos
Consultar los nombres de todos los libros publicados por Ren Woxing.
Directo: obtener datos a través de las tablas mediante nombre_atributo__nombre_atributo_de_otra_tabla (authors__name):
res = models.Book.objects.filter(authors__name="任我行").values_list("title")
Inverso: obtener datos a través de las tablas mediante nombre_clase_en_minúsculas__nombre_atributo_de_otra_tabla (book__title):
res = models.Author.objects.filter(name="任我行").values_list("book__title")

Uno a uno
Consultar el número de teléfono de Ren Woxing.
Directo: mediantenombre_atributo__nombre_atributo_de_otra_tabla (au_detail__tel)obtener datos a través de las tablas.
res = models.Author.objects.filter(name="任我行").values_list("au_detail__tel")

Inverso: mediantenombre_clase_en_minúsculas__nombre_atributo_de_otra_tabla (author__name)obtener datos a través de las tablas.
res = models.AuthorDetail.objects.filter(author__name="任我行").values_list("tel")

