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

class Book(models.Model):
    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:

def add_book(request):
    # 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:

def add_book(request):
    # 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:

def add_book(request):
    # 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:

def add_book(request):
    # 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 objeto
book_obj = models.Book.objects.get(id=10)
author_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

ying = models.Author.objects.filter(name="Ren Yingying").first()
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.

pub = models.Publish.objects.filter(name="Editorial de la Secta Ming").first()
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

author_obj =models.Author.objects.get(id=1)
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.

# Vaciar todos los autores asociados a Dugu Jiujian
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

book = models.Book.objects.filter(pk=10).first()
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

pub = models.Publish.objects.filter(name="Editorial Mingjiao").first()
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

author = models.Author.objects.filter(name="Linghu Chong").first()
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

addr = models.AuthorDetail.objects.filter(addr="Acantilado Heimu").first()
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

book = models.Book.objects.filter(title="Ejemplo").first()
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

author = models.Author.objects.filter(name="Ren Woxing").first()
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

res = models.Book.objects.filter(publish__name="Editorial Ejemplo").values_list("title", "price")

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

res = models.Publish.objects.filter(name="Editorial Ejemplo").values_list("book__title","book__price")
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")

Otras extensiones