Django ORM multi table operation (Advanced)

Keywords: Python SQL Django Mobile Database

 

Django ORM multi table operation (Advanced)

 

1, Create model

Next, we design the corresponding relationship between each table through the book management system.

Through the relationship above, we can define our model class.

from django.db import models


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, "female"),
        (1, "male"),
        (2, "secrecy"),
    )
    gender = models.SmallIntegerField(choices=gender_choices)
    tel = models.CharField(max_length=32)
    addr = models.CharField(max_length=64)
    birthday = models.DateField()

Note that don't forget the steps of django using MySQL database. Only after step-by-step implementation can the database be operated through orm.

2, Operation table record

1. Add some simple data

1. publish table:

insert into app01_publish(name,city,email) 
values
("Huashan Publishing House", "Huashan Mountain", "hs@163.com"), 
("Mingjiao press", "Blackwood cliff", "mj@163.com")
2. author table:

insert into app01_author(name,age,au_detail_id) 
values
("linghu chong",25,1),
("ren woxing",58,2),
("Ren yingying",23,3) 
3. authordatail table:

insert into app01_authordetail(gender,tel,addr,birthday) 
values
(1,13432335433,"Huashan Mountain","1994-5-23"),
(1,13943454554,"Blackwood cliff","1961-8-13"),
(0,13878934322,"Blackwood cliff","1996-5-20") 

2. One to many

# The form of object transmitted by mode I
pub_obj = models.Publish.objects.get(pk=1)
book = models.Book.objects.create(title="Dugu nine Swords", price=180, pub_date="2018-10-23", publish=pub_obj)

# Mode 2 transfer object id Form of
book = models.Book.objects.create(title="Dugu nine Swords", price=180, pub_date="2018-10-23", publish_id=1)

Core: understand the difference between book.publish and book.publish "ID?

3. Many to many

# The form of object transmitted by mode I
book = models.Book.objects.filter(title="Dugu nine Swords").first()
ling = models.Author.objects.filter(name="linghu chong").first()
ying = models.Author.objects.filter(name="Ren yingying").first()
book.authors.add(ling, ying)

# Mode 2 transfer object id Form of
book = models.Book.objects.filter(title="Dugu nine Swords").first()
ling = models.Author.objects.filter(name='linghu chong').first()
ying = models.Author.objects.filter(name='Ren yingying').first()
book.authors.add(ling.pk, ying.pk)

Core: what is book.authors.all()?

Many to many other common API s:

book_obj.authors.remove()      # Remove a specific object from the associated object collection. ======   book_obj.authors.remove(*[])
book_obj.authors.clear()       #Empty the collection of associated objects
book_obj.authors.set()         #Clear before setting

  more

4. Cross table query based on object

1. One to many query (Publish and Book)

Forward query by field publish

# Query the city where the publishing house of the book with primary key 1 is located
book_obj=Book.objects.filter(pk=1).first()
# book_obj.publish is the publishing object associated with the book object with the primary key of 1
print(book_obj.publish.city) 

Reverse query by book table

# Inquire the book name published by Mingjiao Publishing House
publish=Publish.objects.get(name="Mingjiao press")
#publish.book_set.all(): collection of all book objects associated with Mingjiao Publishing House
book_list=publish.book_set.all()    
for book_obj in book_list:
       print(book_obj.title)
2. One to one query (Author and AuthorDetail)

Forward query (by field: Au detail):

# Inquire Linghu Chong's phone number
ling=Author.objects.filter(name="linghu chong").first() print(ling.au_detail.tel)

Reverse query (by table name: author):

# Find out the names of all the authors whose addresses are in heimuya
 
authorDetail_list=AuthorDetail.objects.filter(addr="Blackwood cliff")
for obj in authorDetail_list:
     print(obj.author.name)
3. Many to many query (Author and Book)

Forward query (by field: authors):

# All the authors' names and mobile phone numbers of Dugu Jiujian

book_obj = Book.objects.filter(title="Dugu nine Swords").first()
authors = book_obj.authors.all()
for author_obj in authors: 
    print(author_obj.name, author_obj.au_detail.tel)

Reverse query (by table name: Book Set):

# Query the names of all the books that Linghu rushed out

    author_obj=Author.objects.get(name="linghu chong")
    book_list=author_obj.book_set.all()        #All books related to the author of Ling Huchong
    for book_obj in book_list:
        print(book_obj.title)

Note:

You can override the name of foo'u set by setting the value of related'name in the definitions of ForeignKey() and ManyToManyField. For example, if you make the following changes in the Book model:

publish = models.ForeignKey("Publish", on_delete=models.CASCADE, related_name="book_list")

Then we will see as follows:

 # Query all books published by Mingjiao Publishing House

 publish=Publish.objects.get(name="Mingjiao press")
 book_list=publish.book_list.all()  # Collection of all book objects associated with Mingjiao Publishing House

5. Cross table query based on double underscores

Django also provides an intuitive and efficient way to express the association relationship in the query (lookups), which can automatically confirm the SQL JOIN connection. To do cross relationship queries, use two underscores to link the names of the associated fields between models until you finally link to the model you want.

'''
    Forward query by field, reverse query by table name lowercase to tell ORM engine which table to join
'''
1. One to many query
# Practice:  Check the names and prices of all books published by Mingjiao press(One to many)

    # Forward query by field:publish

    queryResult=Book.objects.filter(publish__name="Ming religionpress").values_list("title","price")

    # Reverse query by table name:book

    queryResult=Publish.objects.filter(name="Ming religionpress").values_list("book__title","book__price")
2. Many to many query
# Practice: Query the names of all the books that Linghu rushed out(Many to many)

    # Forward query by field:authors:
    queryResult=Book.objects.filter(authors__name="linghu chong").values_list("title")

    # Reverse query by table name:book
    queryResult=Author.objects.filter(name="linghu chong").values_list("book__title","book__price")
3. One to one query
# Query Ling Hu Chong's mobile number
    
    # Forward query
    ret=Author.objects.filter(name="linghu chong").values("au_detail__tel") 

  #
inverse query
  ret=AuthorDetail.objects.filter(author__name="linghu chong").values("tel")
4. Advanced practice (continuous span table)
# Practice: Check the names of all the books published by Mingjiao publishing house and the names of the authors


    # Forward query
    queryResult=Book.objects.filter(publish__name="Ming religionpress").values_list("title","authors__name")
    # inverse query
    queryResult=Publish.objects.filter(name="Ming religionpress").values_list("book__title","book__authors__age","book__authors__name")


# Practice: Names of all books published by authors with mobile numbers starting with 132 and the name of the publishing house

# Mode 1: queryResult=Book.objects.filter(authors__au_detail__tel__startswith="132").values_list("title","publish__name")

   # Mode 2:
   ret=Author.objects.filter(au_detail__tel__startswith="132").values("book__title","book__publish__name")

6. Aggregate query and group query

1. aggregate query

Let's feel it through an example.

# Calculate the average price of all books
books = models.Book.objects.aggregate(Avg("price"))
books = models.Book.objects.aggregate(avg_price=Avg("price"))  # Specify the key of the dictionary as avg_

aggregate() is a termination clause of QuerySet, which means that it returns a dictionary containing some key value pairs. The name of the key is the identifier of the aggregate value, and the value is the calculated aggregate value. The name of the key is automatically generated by the name of the field and aggregate function. If you want to specify a name for the aggregate value, you can specify it to the aggregate clause (as in the example above).

If you want to generate more than one aggregate, you can add another parameter to the aggregate() clause. So, if you want to know the maximum and minimum prices of all books, you can query as follows:

from django.db.models import Avg, Max, Min

# Calculate the average, most expensive, and least expensive prices of all books
books = models.Book.objects.aggregate(Avg("price"), Max("price"), Min("price"))
2. Group query annotate

Before we talk about it, let's recall how to query the SQL statements we learned before. Let's compare ORM code to deepen our understanding.

Let's practice the single watch first:

The table structure is:

class Emp(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    salary = models.DecimalField(max_digits=8, decimal_places=2)
    dep = models.CharField(max_length=32)
    province = models.CharField(max_length=32)

Prepare data:

INSERT INTO `bkm`.`app01_emp` (`id`, `name`, `age`, `salary`, `dep`, `province`) VALUES ('1', 'linghu chong', '24', '6000.00', 'Sales Department', 'Henan');
INSERT INTO `bkm`.`app01_emp` (`id`, `name`, `age`, `salary`, `dep`, `province`) VALUES ('2', 'Ren yingying', '18', '8000.00', 'Guangong Department', 'Guangdong');
INSERT INTO `bkm`.`app01_emp` (`id`, `name`, `age`, `salary`, `dep`, `province`) VALUES ('3', 'ren woxing', '56', '10000.00', 'Sales Department', 'Guangdong');
INSERT INTO `bkm`.`app01_emp` (`id`, `name`, `age`, `salary`, `dep`, `province`) VALUES ('4', 'Yue Lingshan', '19', '6000.00', 'Guangong Department', 'Henan');
INSERT INTO `bkm`.`app01_emp` (`id`, `name`, `age`, `salary`, `dep`, `province`) VALUES ('5', 'little dragon maiden', '20', '8000.00', 'Guangong Department', 'Hebei');

Query operation:

# Query the name of each department and the corresponding number of employees
SQL:
select dep, count(1) from emp group by dep;
ORM:
models.Emp.objects.values('dep').annotate(c=Count('id'))

# Query the name of each department and the average salary of the corresponding employee
SQL: 
select dep, avg(salary) from app01_emp GROUP BY dep;
ORM:
models.Emp.objects.values('dep').annotate(a=Avg('salary'))

OK, next. Let's play group query with multiple tables.

The table structure is:

class Emps(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    salary = models.DecimalField(max_digits=8, decimal_places=2)
    dep = models.ForeignKey("Dep", on_delete=models.CASCADE)
    province = models.CharField(max_length=32)


class Dep(models.Model):
    title = models.CharField(max_length=32)

Prepare data:

1. Dep table:

INSERT INTO `bkm`.`app01_dep` (`id`, `title`) VALUES ('1', 'Sales Department');
INSERT INTO `bkm`.`app01_dep` (`id`, `title`) VALUES ('2', 'Guangong Department');

2. Emps table:

INSERT INTO `bkm`.`app01_emps` (`id`, `name`, `age`, `salary`, `province`, `dep_id`) VALUES ('2', 'linghu chong', '24', '8000.00', 'Henan', '1');
INSERT INTO `bkm`.`app01_emps` (`id`, `name`, `age`, `salary`, `province`, `dep_id`) VALUES ('3', 'Ren yingying', '18', '9000.00', 'Guangdong', '2');
INSERT INTO `bkm`.`app01_emps` (`id`, `name`, `age`, `salary`, `province`, `dep_id`) VALUES ('4', 'ren woxing', '57', '10000.00', 'Guangdong', '1');
INSERT INTO `bkm`.`app01_emps` (`id`, `name`, `age`, `salary`, `province`, `dep_id`) VALUES ('5', 'Yue Lingshan', '19', '6000.00', 'Henan', '2');
INSERT INTO `bkm`.`app01_emps` (`id`, `name`, `age`, `salary`, `province`, `dep_id`) VALUES ('6', 'little dragon maiden', '20', '8000.00', 'Hebei', '2');

Query operation:

# Query the name of each department and the corresponding number of employees

SQL:
SELECT app01_dep.title,count(app01_emps.id) FROM app01_emps LEFT JOIN app01_dep ON app01_emps.dep_id = app01_dep.id GROUP BY app01_emps.dep_id;

ORM:
models.Emps.objects.values("dep__title").annotate(c=Count("id"))

# Query the name of each department and the average salary of the corresponding employee

SQL:
SELECT app01_dep.title,avg(app01_emps.salary) FROM app01_emps LEFT JOIN app01_dep ON app01_emps.dep_id = app01_dep.id GROUP BY app01_emps.dep_id;

ORM:
models.Emps.objects.values("dep__title").annotate(a=Avg("salary"))

Summary: the essence of cross table group query is to join related tables into a table, and then group query according to the idea of single table.

3. Inquiry exercise

1. Practice: count the price of the cheapest book of each publishing house

# Exercise: count the price of the cheapest book in each publishing house
    SQL:
    select name, min(app01_book.price)
    from app01_publish LEFT
    JOIN
    app01_book
    on
    app01_book.publish_id = app01_publish.id
    GROUP
    BY
    app01_publish.name;

    ORM:
models.Publish.objects.values("name").annotate(min_price=Min("book__price"))

2. Exercise: count the number of authors of each book

Exercise: count the number of authors of each book
SQL:
SELECT
    title,
    count(NAME)
FROM
    app01_book
LEFT JOIN app01_book_authors ON app01_book.id = app01_book_authors.book_id
LEFT JOIN app01_author ON app01_book_authors.author_id = app01_author.id
GROUP BY
    app01_book.id;

ORM:
models.Book.objects.annotate(num=Count('authors__name')).values("title", "num")

3. Exercise: count the number of authors of each book beginning with "Nine"

# Exercise: count the number of authors of each book beginning with "Nine"
SQL:
SELECT
    title,
    count(NAME)
FROM
    app01_book
LEFT JOIN app01_book_authors ON app01_book.id = app01_book_authors.book_id
LEFT JOIN app01_author ON app01_book_authors.author_id = app01_author.id
WHERE
    app01_book.title LIKE 'Nine%'
GROUP BY
    app01_book.id;

ORM:
models.Book.objects.filter(title__startswith="Nine").annotate(num=Count('authors__name')).values("title", "num")

4. Exercise: count the book names of more than one author

# Exercise: count books by more than one author

SQL:
SELECT
    title,
    count(NAME) AS num
FROM
    app01_book
LEFT JOIN app01_book_authors ON app01_book.id = app01_book_authors.book_id
LEFT JOIN app01_author ON app01_author.id = app01_book_authors.author_id
GROUP BY
    app01_book.id
HAVING
    num > 1;

ORM:
models.Book.objects.annotate(num_author=Count("authors__name")).filter(num_author__gt=1).values("title")

5. Exercise: sort the query set by the number of authors of a Book

# Exercise: pair query sets according to the number of authors in a Book QuerySet Sort

SQL:
SELECT
    title,
    count(author_id) AS num
FROM
    app01_book
LEFT JOIN app01_book_authors ON app01_book.id = app01_book_authors.book_id
LEFT JOIN app01_author ON app01_author.id = app01_book_authors.author_id
GROUP BY
    app01_book.id
ORDER BY
    num;

ORM:
models.Book.objects.annotate(num_author=Count("authors__name")).order_by("num_author").values("title", "num_author")

6. Exercise: query the total price of books published by each author

# Exercise: find out the total price of each author's book

SQL:
SELECT
    NAME,
    sum(price)
FROM
    app01_author
LEFT JOIN app01_book_authors ON app01_author.id = app01_book_authors.author_id
LEFT JOIN app01_book ON app01_book.id = app01_book_authors.book_id
GROUP BY
    app01_author.id;

ORM:
models.Author.objects.annotate(total=Sum("book__price")).values('name', 'total')

7. F query and Q query

1. F query

In all of the above examples, the filter we constructed simply compares a field value to a constant. If we want to compare the values of two fields, what should we do?

Django provides F() to make such a comparison. The instance of F() can reference fields in the query to compare the values of two different fields in the same model instance.

# Query the person whose salary is greater than age
from django.db.models import F, Q

models.Emp.objects.filter(salary__gt=F('age'))

Django supports addition, subtraction, multiplication, division and modulo operations between F() objects and between F() objects and constants.

# Query people whose salary is less than twice the age value

models.Emp.objects.filter(salary__lt=F("age") * 2)

F function can also be used for modification, such as increasing the price of each book by 100 yuan

models.Book.objects.update(price=F('price') + 100)
2. Q query

Keyword parameter queries in filter() AND other methods are "AND" together. If you need to perform more complex queries (such as OR statements), you can use the Q object.

from django.db.models import Q

Q(title__startswith="Nine")

Q objects can be combined with &, | and  ̄ (and or not) operators. When an operator is used on two Q objects, it produces a new Q object.

# Search for books whose price is more than 300 or whose name begins with nine
    
models.Book.objects.filter(Q(price__gt=300)|Q(title__startswith="Nine"))

Equivalent to the following SQL where clause:

WHERE (`app01_book`.`price` > 300 OR `app01_book`.`title` LIKE BINARY 'Nine%') 

For comprehensive use, see the example:

# Search for books with a price greater than 300 or not march 2019

models.Book.objects.filter(Q(price__gt=300)|~Q(Q(pub_date__year=2019)&Q(pub_date__month=3)))

Posted by bough on Fri, 24 Apr 2020 22:45:19 -0700