Django ORM多表操作 正向反向查询语法 中介模型 反向生成models

ORM的注意事项

  • nid = models.AutoField(primary_key=True) ID字段是自动添加的,需要自定义变量名可以自己填写.
  • 对于外键字段,Django会在字段名上添加_id来创建数据库中的别名
  • 外链字段Foreignkey有一个null=true的设置,它允许外链接受空值null,可以给赋空值None.

Foreignkey
-to:与那张表建立关联
-to_field:对表中的某个字段

1 设置了关联后,对于基于对象的跨表反向查询的时候,直接使用替换的aa即可

-related_name:反向操作时,使用的字段名,用于代替原反向查询时的’表名_set’

2 同上

-related_query_name:反向查询操作时,使用的连接前缀,用于替换表名

多表关系的操作概要

  • 1对1的关系在两张表任意一张建立关联字段并且加Unique,比如
作者表 author
 id  name  age
  1  alex  18
  2  blex  30
  3  clex  25

作者个人信息表 authordetail
  id  addr  gender  tel  author_id(Unique)
  1    北京  男      123     1
  2    南京  女      1234    2
  3    上海  人妖    3311    3

create table authordetail(
  id int primary key auto_increment,
  addr varchar(20),
  gender enum("male","female"),
  tel int(11)
  author_id int unique,

);

  • 1对多的关系 需要在多个关系的表中建立关联字段
出版社 publish
 id  name      book
  1  北京出版社  18
  2  南京出版社  30
  3  天津出版社  25

create table publish(
  id int primary key auto_increment,
  name varchar(20)
);


书籍  book  在书籍和出版社对应关系中,一个出版社会出版多本书籍,因此把多个出版社id放在book表中创建一对多的关系,并且==要建立约束==,创建关系是为了查询,创建约束是为了防止产生脏数据
  id bookname  price  publish_id
  1  python      10        1
  2  java        20        1
  3  go          58        2
  4  php         79        3
 
create table book(
  id int primary key auto_increment,
  bookname varchar(20),
  price    decimal(8,2),
  publish_id int,
  foreign key(publish_id) reference book(id),


);
  • 多对多的关系 创建第三张关系表
书籍 book
 id  name      price    publish_id
  1  python      18          1
  2  java        30          1
  3  go          25          2

create table book(
  id int primary key auto_increment,
  name varchar(32),
  price decimal(5,2),
  publish_id int,
  foreign key (publish_id) reference publish(id),
);


作者表 author
 id  name  age
  1  alex  18
  2  blex  30
  3  clex  25

create table author(
  id int primary key auto_increment,
  name varchar(32),
  age int,
);

做着书籍关系表 book2auther

  id  book_id  author_id
  1      1        1
  2      1        2
  3      2        1
  4      3        2
create table book2auther(
  id int primary key auto_increment,
  book_id int,
  author_id int,
  foreign key (book_id) reference book(id),
  foreign key (author_id) reference author(id),
);



create table publish(
  id int primary key auto_increment,
  name varchar(20)
);

那么上面的sql语句在ORM中是怎么创建的呢,下面拿着SQL语法 举例在ORM中的语法

1. 出版社和书本的一对多关系


on_delete有6个可选值,分别是:
CASCADE 删除级联,当父表的记录删除时,子表中与其相关联的记录也会删除。即:当一个老师被删除时,关联该老师的学生也会被删除。
PROTECT 子表记录所关联的父表记录被删除时,会报ProtectedError异常。即:当一个学生所关联的老师被删除时,会报ProtectedError异常。
SET_NULL 子表记录所关联的父表记录被删除时,将子表记录中的关联字段设为NULL,注意:需要允许数据表的该字段为NULL。
SET_DEFAULT 子表记录所关联的父表记录被删除时,将子表记录中的关联字段设为一个给定的默认值。
DO_NOTHING 子表记录所关联的父表记录被删除时,什么也不做。
SET() 设置为一个传递给SET()的值或者一个回调函数的返回值,该参数用得相对较少。

2. 作者和作者详情的一对一关系

3. 书本和作者多对多的关系


我们可以看下手动创建的Book2Author 和ManyToMany创建的 book_author表,实现的效果是一样的.

orm一对多表之添加记录操作

class Publish(models.Model):
    nid = models.AutoField(primary_key=True)
    publish = models.CharField(max_length=32)
    def __str__(self):
        return self.publish

class book(models.Model):
    id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=20)
    price = models.DecimalField(max_digits=8, decimal_places=2)
    pub_date = models.DateField()
    publish = models.ForeignKey(to="Publish", to_field="nid",on_delete=models.CASCADE)

出版社和图书的对应关系为 一个出版社对应多本书,把对应关系写在book类中
出版社数据添加不多说,因为很简单,pub = models.Publish.objects.create(publish="南京出版社")
图书中的添加语法有2种,因为book中的publish在数据库中会自动进行字符串拼接为publish_id

  • 第一种book = models.book.objects.create(title="c++",price=18,pub_date = "2012-05-07",publish_id=2) publish_id直接写死
  • 第二种如下,此时book.title是java,book.price是28,但是book.publish 其实就是pub这个对象了,他包含了出版社的信息,所以book.publish.publish就是出版社的名字(南京出版社)
pub = models.Publish.objects.filter(publish="南京出版社").first()  #先对Publish表进行筛选出南京出版社的对象,再把这个对象放到publish的参数中去,等同于publish_id=南京的id
book = models.book.objects.create(title="java",price=28,pub_date = "2014-05-07",publish=pub)

orm多对多表之添加记录操作

class Book(models.Model):
    id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=8, decimal_places=2)
    publish = models.ForeignKey(to="Publish", to_field="pid", on_delete=models.CASCADE)
    author = models.ManyToManyField(to="Author")

class Author(models.Model):
    aid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    author_detail = models.OneToOneField(to="Author_detail", to_field="aid", on_delete=models.CASCADE)

多对多添加也是第一步也是先获取一个对象,比如作者和书籍的关系,因为author这个字段在Book表中进行多对多的表(app1_book_author)的添加,我们先获取author对象

  1. alex = models.Author.objects.filter(name="alex").first() blex = models.Author.objects.get(name="blex") alex和blex就是作者的model对象
  2. 先创建Book的id title price 以及一对多的Publish字段 bk = models.Book.objects.create(title="java",price=29,publish_id=2) #书名叫Java的对象
  3. 通过Book下的author字段添加多对多关系,通过add()方法添加,可以加对象,也可以直接加id号,也可以用列表形式,除了添加还有remove方法和all()方法可以删除和查询多对多关系
    bk.author.add(alex,blex) #利用book类下的author字段,这个字段就是获取了manytomany的对象
    bk.author.add(1,2)
    bk.author.add(*[alex,blex])   

orm多对多表之更改记录操作

比如还是作者和书籍的多对多关系,第一步还是先获取筛选对象 bk = models.Book.objects.create(title="java", price=28.23, publish=pub)
然后有2种方法

第一种方法
bk.author.clear() 先清除他们的关系
bk.author.add(3,4)在按照上面新增一个关系

第二种方法用set(list) set会在新增之前把原先关系删除,而且里面默认放的就是列表,也不用加*
bk.author.set([3,4])

orm一对多和多对多 基于对象跨表查询

正向和反向查询语法总结

数据库查询有2种,一种是子查询,一个子查询作为另一个查询语句的结果,第二种是join查询,把N张表通过关联组成一张大表,然后再提取里面的数据.

基于对象查询

正向
查询按字段
stu = Student.objects.get(name="李四")
stu.stu_detail.tel
反向
1. 按表名小写_set或者related_name字段
  course = Course.objects.get(title="近代史")
  course.student_set.all()      如果cousr模型的student设置了related_name=students 则查询语句为course.students.all()
2. 按表名或者related_name字段
  stu_detail = StudentDetail.objects.get(tel="110")
  print(stu_detail.student.name)
反向查询分情况决定是用表名小写_set或者表名小写.  先要了解_set 代表查询结果是一个queryset,
当两张表是一对一关联的话,查询结果必然是一个结果,不可能是一个queryset,所以这种情况是用表名小写反向查询.
当一对多的关联字段反向查询则用表名小写_set

基于join的跨表查询

正向
关联字段__字段
Student.objects.filter(age__gt=22).values("name","clas__name")

反向
表名_字段或者related_name字段__字段
查询年龄大于22的学生的姓名以及所在名称班级
Clas.objects.filter(student_list__age__gt=22).values("student_list__name","name")
查找手机号是110的学生的姓名和所在班级名称  stu是related_name字段
StudentDetail.objects.filter(tel="110").values("stu__name","stu__clas__name")

跨表查询对应的就是第一种子查询语句.跨表查询分 正向查询和反向查询.

正向查询

1.对象查找(跨表) 对象.关联字段.字段
要点:先拿到对象,再通过对象去查对应的外键字段,分两步
示例

book_obj = models.Book.objects.first()  # 第一本书对象(第一步)
print(book_obj.publisher)  # 得到这本书关联的出版社对象
print(book_obj.publisher.name)  # 得到出版社对象的名称

2.字段查找(跨表) 关联字段__字段
示例

models.Book.objects.all().values("publisher__name")
#拿到所有数据对应的出版社的名字,神奇的下划线帮我们夸表查询

反向查询

1.对象查找 obj.表名_set 要点:先拿到外键关联多对一中的某个对象,由于外键字段设置在多的一方,所以这里还是借用Django提供的双下划线来查找

示例

publisher_obj = models.Publisher.objects.first()  # 找到第一个出版社对象
books = publisher_obj.book_set.all()  # 找到第一个出版社出版的所有书
titles = books.values_list("title")  # 找到第一个出版社出版的所有书的书名

结论:如果想通过一的那一方去查找多的一方,由于外键字段不在一这一方,所以用__set来查找即可

2.字段查找 表名__字段 要点:直接利用双下滑线完成夸表操作
示例

titles = models.Publisher.objects.values("book__title")

正向查询按字段:关联字段在A表, 通过A查B, 反向查询按表名(表名小写_set):关联字段在A表, 通过B查A

    #一对多 正向查询,查python书籍的出版社名字
    bk = models.Book.objects.get(title="python")
    print(bk.publish.name)

    # 一对多 反向查询,查南京出版社出版的所有书籍
    pub=models.Publish.objects.get(name="南京出版社")
    print(pub.book_set.all())  #表名_set 返回的是个queryset对象集合

    # 多对多 正向查询,查python书籍对应的所有作者名字
    bk = models.Book.objects.get(title="python")
    print([i.name for i in bk.author.all()])

    # 多对多 反向查询,查blex作者对应的所有书籍名称
    au = models.Author.objects.get(name="blex")
    print([i.title for i in au.book_set.all()])

orm一对一 基于对象跨表查询

正向查询和一对多 多对多一样,但是反向查询有点不同,因为是一对一,不会有多个结果,
所以,结果也不会是queryset,语法也不用是小写表名_set,而是小写表名,例如 auth_detail.author.name

# 一对一
    # 正向查询
    au = models.Author.objects.filter(name="blex").first()
    print(au.author_detail.add)
    # 反向查询
    auth_detail = models.Author_detail.objects.filter(add="鄞州区").first()
    print(auth_detail.author.name)

orm 一对多和多对多查询 基于join查询

    #跨表inner join查询 都是在orm语法中就是和values方法
    bk = models.Author.objects.filter(name="alex").values("age")
    bk2 = models.Author.objects.filter(name="alex").values_list("age", "author_detail")
    print(bk)
    print(bk2)

    #基于双下划线的跨表查询 (join查询)  一对多.
    #方式一     正常查询按表名,不过表名语法略有不同,是表名__字段,values里面 publish__name意思是Publish表中的name字段

    bk3=models.Book.objects.filter(title="java").values("publish__name")
    print(bk3)
    #方式二 反向查询,通过出版社过滤book表中titile为python的对象,然后提取出版社的name字段
    bk4 = models.Publish.objects.filter(book__title="python").values("name")
    print(bk4)

    # 基于双下划线的跨表查询(join查询) 多对多查询 查询java书籍的所有作者和alex写的书籍
    #方式一 正向查询按字段
    bk5 = models.Book.objects.filter(title="java").values("author__name")
    print(bk5)
    # 方式二 反向查询按表名__字段 通过作者查询书名
    auth = models.Author.objects.filter(name="alex").values("book__title")
    print(auth)

基于双下划线的跨表查询(join查询) 一对一查询的正向查询和反向查询语句和上述并无二样. 不做举例.

基础双下划线的连续跨表查询(join查询)

#连续跨表查询 ,查到城市以宁波开头的作者出版过的书籍和对应的出版社名字
    #方式一 正向查询
    #假如以book为起始对象,book关联了作者表和出版社表,但是并未直接关联作者详情的表.我们需要多表跨表查询获取对应的数据
    #author__author_detail__city__startswith 就是join了author author_detail2张表,然后根据city为宁波开头的条件过滤出来获得bk对象
    #"title","publish__name"  bk对象有titile字段,可以直接获取,但是publish需要通过bk的publish_id去对应到Publish表查询,属于正向查询,按字段查询.
    bk = models.Book.objects.filter(author__author_detail__city__startswith="宁波").values("title","publish__name")
    print(bk) #QuerySet [{'title': 'java', 'publish__name': '南京出版社'}, {'title': 'python', 'publish__name': '北京出版社'}]>

    #还是查到城市以宁波开头的作者出版过的书籍和对应的出版社名字,这次以author为基表进行反向查询
    # author和author_detail有正向的连接的author_detail_id字段,所以filter直接筛选,获取到author对象
    # 接下来通过author对象获取book名和出版社名,但是author对象和book是反向查询关系,通过book表的title和book表下publish字段关联的Publish的name字段获取到最终结果.
    auth=models.Author.objects.filter(author_detail__city__startswith="宁波").values("book__title","book__publish__name")
    print(auth)

基础双下划线的跨表查询(join查询) 查询datetime数据类型下的年月日

我们的ORM中字段如果是Date或者DateTime类型 比如 create_time = models.DateTimeField()
我们也可以用双下划线的模式对到这个create_time下的 年月日 时分秒字段做字段过滤 比如 xxx.object.filter(create_time__year=2021,create_time__month=06)

中介模型

ManyToManyField 表示小组和成员之间的多对多关系。但是,有时你可能想知道更多成员关系的细节,比如成员是何时加入小组的。 对于这些情况,Django 允许你指定一个中介模型来定义多对多关系。 你可以将其他字段放在中介模型里面。源模型的ManyToManyField 字段将使用through` 参数指向中介模型。对于上面的音乐小组的例子,代码如下:

from django.db import models
 
class Person(models.Model):
    name = models.CharField(max_length=128)
 
    def __str__(self):              # __unicode__ on Python 2
        return self.name
 
class Group(models.Model):
    name = models.CharField(max_length=128)
    members = models.ManyToManyField(Person, through='Membership')
 
    def __str__(self):              # __unicode__ on Python 2
        return self.name
 
class Membership(models.Model):
    person = models.ForeignKey(Person)
    group = models.ForeignKey(Group)
    date_joined = models.DateField()
    invite_reason = models.CharField(max_length=64)

与普通的多对多字段不同,你不能使用add,create,set等赋值语句

>>> ringo = Person.objects.create(name="Ringo Starr")
>>> paul = Person.objects.create(name="Paul McCartney")
>>> beatles = Group.objects.create(name="The Beatles")
>>> m1 = Membership(person=ringo, group=beatles,
...     date_joined=date(1962, 8, 16),
...     invite_reason="Needed a new drummer.")
>>> m1.save()
>>> beatles.members.all()
[<Person: Ringo Starr>]
>>> ringo.group_set.all()
[<Group: The Beatles>]
>>> m2 = Membership.objects.create(person=paul, group=beatles,
...     date_joined=date(1960, 8, 1),
...     invite_reason="Wanted to form a band.")
>>> beatles.members.all()
[<Person: Ringo Starr>, <Person: Paul McCartney>]

无法使用add,create,set等示范

# THIS WILL NOT WORK
>>> beatles.members.add(john)
# NEITHER WILL THIS
>>> beatles.members.create(name="George Harrison")
# AND NEITHER WILL THIS
>>> beatles.members = [john, paul, ringo, george]

为什么不能这样做? 这是因为你不能只创建 PersonGroup之间的关联关系,你还要指定 Membership模型中所需要的所有信息;而简单的addcreate 和赋值语句是做不到这一点的。所以它们不能在使用中介模型的多对多关系中使用。此时,唯一的办法就是创建中介模型的实例。

remove()方法被禁用也是出于同样的原因。但是clear() 方法却是可用的。它可以清空某个实例所有的多对多关系:

>>> # Beatles have broken up
>>> beatles.members.clear()
>>> # Note that this deletes the intermediate model instances
>>> Membership.objects.all()
[]

反向生成models

Django较为适合原生开发,即通过该框架搭建一个全新的项目,通过在修改models.py来创建新的数据库表。但是往往有时候,我们需要利用到之前的已经设计好的数据库,数据库中提供了设计好的多种表单。那么这时如果我们再通过models.py再来设计就会浪费很多的时间。所幸Django为我们提供了inspecdb的方法。他的作用即使根据已经存在对的mysql数据库表来反向映射结构到models.py中.

我们在展示django ORM反向生成之前,我们先说一下怎么样正向生成代码。

正向生成,指的是先创建model.py文件,然后通过django内置的编译器,在数据库如mysql中创建出符合model.py的表。

反向生成,指的是先在数据库中create table,然后通过django内置的编译器,生成model代码。

python manage.py inspectdb > models文件名 [不写表名默认生成所有表]
posted @ 2021-08-15 12:22  零哭谷  阅读(179)  评论(0编辑  收藏  举报