python学习:Django的ORM(Model)(3)

Django的ORM

ORM

        ORM:对象关系映射,是通过使用描述对象和数据库之间映射的元数据,将面向对象语言程序中的对象自动持久化到关系数据库中。

        表与表直接关系:一对一,一对多,多对多。

        Python的ORM,Python的类操作数据库表,类对应数据库中的表,类对象就是表中一条记录。

 

Django默认使用sqlite数据库。

setting.py中可以看到:

   DATABASES = {

    'default': {

        'ENGINE': 'django.db.backends.sqlite3',

        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),

    }

}

 

Mysql驱动

        Mysqldb(python2使用)

        Pymysql(python3使用)

        使用mysql需要安装对应驱动。

 

Django创建数据库表

在models.py中编写python类,类继承models.Model类。

示例:

class Book(models.Model):

    name = models.CharField(max_length=4);

    price = models.FloatField();

    pub_date=models.DateField();

    count = models.IntegerField();

 

终端执行:python manage.py makemigrations

生成migrations文件夹中文件。

然后执行:python manage.py migrate

在数据库中生成对应表。

可以在migrations文件夹中看到0001_initial.py文件,可以看到我们book类的属性,如果没有设置主键,自动添加一个id主键。

operations = [

    migrations.CreateModel(

        name='Book',

        fields=[

            ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),

            ('name', models.CharField(max_length=4)),

            ('price', models.FloatField()),

            ('pub_date', models.DateField()),

            ('count', models.IntegerField()),

        ],

    ),

]

我们使用的是默认数据库,sqlite3。可以使用SqliteStudio打开db.salite3文件,可以看到我们创建的表和Django自动生成的其他数据表。表名一般是你的AppName+类名。

 

 

使用Mysql数据库

修改setting.py文件:

# DATABASES = {

#     'default': {

#         'ENGINE': 'django.db.backends.sqlite3',

#         'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),

#     }

# }

#使用mysql数据库

DATABASES = {

    'default': {

        'ENGINE': 'django.db.backends.mysql',

        #数据库名

        'NAME': 'python_test',

        #用户名

        'USER':'root',

        #密码

        'PASSWORD':'tlj123456',

        #主机,默认localhost

        'HOST':'',

        #端口

        'PORT':'3306'
    }
}

 

在项目文件夹,_init_.py写入语句,声明使用mysql引擎,因为默认项目使用sqlite3

import pymysql

pymysql.install_as_MySQLdb()

然后执行python manage.py makemigrations和python manage.py migrate创建数据库表。

 

 

可以看到我们创建的表helloapp_book。

 

Django数据库增删改查

新增数据:

通过新建类对象,调用对象的save方法。

#方式1

#新建对象,一个对象对应一条记录

book = Book(name="数据库基础",price=10.33,pub_date="2017-2-22",count=20);

#保存记录到数据库

book.save();

使用类Book.objects.create()方法:

#方式2

Book.objects.create(name="python基础",price=22.33,pub_date="2019-2-22",count=50)

 

更新数据:

#方式1

#更新数据

Book.objects.filter(name="数据库基础").update(price="20.33")

 

#方式2

book = Book.objects.filter(name="数据库基础");

#book为querySet类型,数据集合

for b in book:

    b.price=33.22;

    b.save();

 

get方式获取的是单条记录,返回对应对象

#方式3

book = Book.objects.get(id=1);

book.price ="33.34";

book.save();

 

删除数据:

#查询出对应记录删除

Book.objects.filter(name="数据库基础").delete();

 

查询数据:

使用all()查询所有记录,返回querySet对象

#查询所有结果

book_list = Book.objects.all();

使用filter(),筛选指定条件,返回querySet对象

#查询符合条件结果

Book.objects.filter(name="python基础",count=50)

切片查询部分记录,返回querySet对象

#切片查询指定条数记录,正数正序,负数倒序

book_list = Book.objects.all()[::2];

获取第一条,最后一条记录,返回一个对应对象

#查询第一条,最后一条记录

book = Book.objects.first();

book = Book.objects.last();

get查询一条符合条件记录

#查询一条指定记录

book = Book.objects.get(id=1);

 

只查询出指定栏位values()

#查询出指定栏位,返回一个字典集合

result = Book.objects.filter().values("name","price")

# 查询出指定栏位,返回一个元组列表集合

result = Book.objects.filter().values_list("name", "price")

 

查询出不符合条件的记录exclude()

#查询出不符合条件的记录

book_list = Book.objects.exclude(name="python基础")

 

使用distinct()去除重复字段

#去除重复字段

book_list = Book.objects.all().values("name","price","count").distinct();

使用count()获取记录数量

#获取记录数量

count_num = Book.objects.all().count();

 

模糊查询:

使用语法:字段名_关键字

关键字:

lt 小于 ;

gt 大于;

contains 包含 ;

icontains 包含(不区分大小写);

in 在一个数组中;

range 在range值范围内;

startwith,endwith,istartwith,iendwith:以什么开始和结尾,是否区分大小写。

#模糊查询,lt 小于,gt 大于,contains 包含,icontains 包含(不区分大小写)

book_list = Book.objects.filter(name__contains="基础",price__lt=20)

 

创建一对多表外键关系,使用ForeignKeyField

示例:

# Create your models here.

class Book(models.Model):

    name = models.CharField(max_length=20);

    price = models.FloatField();

    pub_date=models.DateField();

    count = models.IntegerField();

# #默认创建时pub,变成pub_id,如果pub_id数据库表变成pub_id_id,一般不用这种。

#pub = models.ForeignKey('Publish',to_field='id', on_delete=models.CASCADE);

# #将整个对象作为外键(创建时自动将表主键作为外键)

  publish = models.ForeignKey('Publish',on_delete=models.CASCADE)

 

class Publish(models.Model):

    id = models.IntegerField(primary_key=True,auto_created=True);

    name = models.CharField(max_length=20);

    addr = models.CharField(max_length=30);

 

一对多添加数据

#一对多添加数据

#方式1,直接添加一条记录

book = Book(name="数据库基础",price=10.33,pub_date="2017-2-22",count=20,publish_id=1);

#保存记录到数据库

book.save();

 

#方式2,通过对象添加数据

publish = Publish.objects.filter(id = 1)[0];

Book.objects.create(name="c语言",price="22.3",pub_date="2012-02-12",count="22",publish=publish);

 

一对多连接查询

查询book的出版商

#方式1

#这种方式可以直接使用publish外键获取到Publish值

book = Book.objects.get(id = 2);

#book的外键pbulish是一个Publish类对象

print(book.publish);

 

#方式2

#使用外键关系id,分步查询

book = Book.objects.get(id=2);

publish = Publish.objects.get(id = book.publish_id);

return render(request,"books.html",locals());

查询出版商出版的书籍

#方式1,Book有publish类对象外键

publish = Publish.objects.get(name="新华出版社");

book = Book.objects.filter(publish=publish)

 

#方式2,类名_set,使用publish.book_set.all(),获取对应书籍信息

publish = Publish.objects.get(name="新华出版社");

#获取的是一个QuerySet,

print(publish.book_set.all())

book_list = publish.book_set.all()

 

#方式3,双下划线访问外键对象属性,要求:Book有publish类对象外键,使用__双下划线,访问类对象属性

#查找出版社出版书籍

book_list=Book.objects.filter(publish__name="新华出版社")

#查找对应书籍出版社

publish = Publish.objects.filter(book__name="c基础");

#同样查找对应书籍出版社,publish的类对象属性可以作为筛选列

ret = Book.objects.filter(name="c基础").values("publish__name")

 

一对多关系整理:

    书籍和出版社的关系是一对多的关系,规定一本书只能对应一个出版社,而一个出版社可以出版多本书。Book书籍类和Publish出版社类,Book有个外键publish对应Publish出版社类(publish = models.ForeignKey('Publish',on_delete=models.CASCADE))。体现在数据库表中:Book表中有个栏位publish_id,外键约束对应Publish表的id栏位。

1、正向查询

    通过Book表查询Publish表信息,就是查询书籍对应出版社信息,由于Book类有publish这个外键,直接可以连接操作publish表。可以通过对象获取,也可以通过‘__‘双下划线获取。这种叫做正向查询。

对象方式操作:

获取所有书籍:

book_list = Book.objects.all();

通过对象方式获取

循环获取所有书籍的出版社:

for book in book_list:

               #book的名字

               book.name

       #book.publish是一个publish对象,.name获取publish名字

               book.publish.name

通过”__”双下划线操作:

       使用filter获取指定条件书籍:

book =Book.objects.filter(name = “python基础”,publish__name = “长江出版社”).first()

       使用values限制查询列:

book = Book.objects.values(“name”,”publish__name”);

       其中publish__name就是通过“__“双下划线访问外键对应表属性。

2、反向查询

       通过Publish出版社表查询Book的信息,由于Publish中没有Book表的任何关联信息,Publish只是被动被Book表外键关联。通过Publish查询Book的信息,比如查询出版社出版的所有书籍,这种查询叫反向查询。

       对象方式操作:

     #获取出版社对象

       publish = Publish.objects.filter(name = “长江出版社”).first();

       #出版社名

       print(publish.name)

      #通过book_set获取到了所有书籍信息,book_set就相当于Book.objects

       print(publish.book_set.all())

       #在filter,values中使用

       #filter条件使用“__“双下划线

       Publish.objects.filter(name = “长江出版社”, book_set__name = “python基础”)

       #values查询列使用”__”双下划线

       Publish.objects.values(“name”,”book_set__name”)

    Djaongo中默认反向查找使用小写类名_set可以反向访问到类对应表信息。但是这个是可以修改的。定义ForeiginKey时,将related_name属性赋予其他值。

    例如不使用book_set,需要修改创建表

 publish = models.ForeignKey('Publish',on_delete=models.CASCADE,related_name =”books”),

   修改后就可以使用publish.books.all()获取book信息。

 

多对多数据库连接查询

       当删除掉app的migrations文件夹中所有py文件后,执行python manage.py makemigrations 成功,但是执行python manage.py migrate创建没效果。

解决方式:

1、首先将各个子应用 app 下 migrations 文件夹中除 init.py 之外,其他文件全部删掉。

2、python manage.py dbshell 进到数据库中,

删除 django_migrations 表中,迁移时出错的子应用的所有记录

delete from django_migrations where app='your_appname';

3、python manage.py makemigrations

4、python manage.py migrate --fake

这样执行依然没用?重新创建数据库吧!

 

创建多对多表关系, models.ManyToManyField()。Django会自动生产多对多的那个关系表,表名为app名_多对多关系表名_多对多关系表名。

这里示例中的是ormApp_book_author。

示例:

from django.db import models

# Create your models here.

class Book(models.Model):

    name = models.CharField(max_length=20);

    price = models.FloatField();

    pub_date=models.DateField();

    count = models.IntegerField();

    # #默认创建时pub,变成pub_id,如果pub_id数据库表变成pub_id_id

    # pub = models.ForeignKey('Publish',to_field='id',default=1,on_delete=models.CASCADE);

    #将整个对象作为外键

    publish = models.ForeignKey('Publish',on_delete=models.CASCADE)

    #多对多关系

    author = models.ManyToManyField("Author");

 

class Publish(models.Model):

    name = models.CharField(max_length=20);

    addr = models.CharField(max_length=30);

 

class Author(models.Model):

    name = models.CharField(max_length=10);

    age = models.IntegerField();

 

多对多关系数据添加

#多对多关系添加数据

#通过对象绑定关系

book_obj =  Book.objects.get(id=1);

#book_obj.author保存的是这本书对应作者的集合,使用all()获取集合所有对象

print(book_obj.author.all())

print(type(book_obj.author.all()))

#获取一个作者

author_obj = Author.objects.get(id = 1);

#book和作者关系绑定,写入多对多关系表ormApp_book_author

book_obj.author.add(author_obj);

#book和作者关系解除绑定

book_obj.author.remove(author_obj);

#多个作者添加删除

#获取多个作者,author_objs是一个集合,需要使用*添加

author_objs = Author.objects.all();

book_obj.author.add(*author_objs);

 

多对多查询

#多对多查询

#方式1:表名_set方式

#通过author,拿到author对应book信息

author_obj = Author.objects.get(id = 1);

#和一对多一样,通过book_set获取

print(author_obj.book_set.all())

 

#方式2:通过表名__列名作为筛选条件

#查询author对应book信息

book_list = Book.objects.filter(author__id = 1)

 

以上是Django自动创建的第三张关系表。

我们也可以自己创建第三张关系表类,但是不推荐自己建立第三张关系表。

# class Book_Author(models.Model):

#     book = models.ForeignKey("Book",on_delete=models.CASCADE);

#     author = models.ForeignKey("Author",on_delete=models.CASCADE);

#添加关联数据

Book_Author.objects.create(book_id=1,author_id =2);

多对多查询

#通过书籍查询作者信息

book_obj = Book.objects.create(id=2);

#通过书籍对象,获取作者信息

authors = book_obj.book_author_set.all()[0].author

#使用双下划线,通过表名__关联表名__关联表列名,作为条件。获取对应作者的书籍

book_list = Book.objects.filter(book_author__author__name=”作者名”)

 

多对多关系整理

       书籍表Book和作者表Author,一本书可以对应多个作者,一个作者可以写多本书,所以Book和Author直接是多对多关系。

  表定义多对多关系在Book表中建立字段:

author = models.ManyToManyField("Author")

       ManyToManyField定义多对多关系。数据库中会自动生成对应的多对多关系的第三张表。其中关系表名:应用名_book_author。数据库中第三张关系表有book_id字段和author_id这两个外键,确定Book和Author表之间关系。

1、正向查询

   Book表拥有author这个ManyToManyField字段,通过Book查询作者信息,这种查询叫做正向查询。

对象操作:

#添加记录

book = Book.objects.filter(id = 1).first();

#通过author操作add,添加操作

#为书籍1,添加一个作者2

book.author.add(2)

#通过author操作set,更新操作

#将Book和Author关系设置为1,1 和1,2,

#就是将书籍1和作者1和2关联,书籍1如果之前对应作者3会被删除

book.author.set([1,2])

#通过author对象操作

book = Book.objects.all().first();

  #通过author字段操作作者表

print(book.author.all())

#filter和values中使用“__“双下划线访问关系表属性

book = Book.objects.filter(id = 1,author__name = “tom”).first();

book_list = Book.objects.values(“id”,”author__name”);

2、反向查询

    Author表没有ManyToManyField字段,作为被动多对多连接表可以通过关系表小写表名_set作为操作字段访问关系表,这里Book表作为关系表,Author可以通过book_set作为对象操作Book表。这里也可以在创建Book表的author字段设置字段的related_name属性赋予其他值。

  例如:

#设置related_name,反向查询通过Author表查询Book表时,使用books操作Book表相关信息

author = models.ManyToManyField("Author",related_name="books");

  #查询作者对应书籍

  author = Author.objects.filter(id = 1).first();

  #设置了related_name,直接使用books或书籍名,否则使用book_set

  print(author.books.name)

 

对象操作:

 #通过book_set添加关系

   author = Author.objects.filter(id = 2).first();

   #让作者和book 2添加关系

   author.book_set.add(2);

   #filter和values使用”__”双下划线查找

   author = Author.objects.filter(id = 2, book_set__name = “python基础”).first();

   author_list = Author.objects.values(“id”,”book_set__name”)

 

Django聚合查询和分组查询

使用aggregate()聚会查询。

导入模块

from django.db.models import Avg,Sum,Max,Min
示例:
#聚合函数

#查询所有书籍平均值

ret = Book.objects.all().aggregate(Avg("price"));

print("书籍平均值:",ret)

#查询author的id为1的书籍价格总和

ret = Book.objects.filter(author__id = 1).aggregate(Sum("price"));

print("id为1作者书籍价格总和:", ret)
使用annotate()分组查询。
示例:
#分组作者,作者书籍价格总和

ret = Book.objects.values("author__name").annotate(Sum("price"));

print("按作者名分组,每个作者书价格总和:",ret)

 

Django的F和Q查询

需要导入F,Q模块

from django.db.models import F,Q

示例:

#将book的价格加上10

#这样写price识别不出来,需要用F对象将列包起来

#Book.objects.all().update(price = price + 10)

Book.objects.all().update(price = F("price") +10);

 

#查询时可以将条件使用Q对象包起来,可以使用与或非运算

#,与运算  | 或运算 ~ 非运算

#Q查询和普通可以混用,但Q查询必须在前面

# ret = Book.objects.filter(Q(price = 30) | Q( name = "人工智能"))

# print("Q查询与或非运算:", ret);

ret = Book.objects.filter(Q(name="python基础"),price = 150.23)

print("Q查询混合:",ret);

#没有使用iterator,所有记录都到内存,可以重复使用

for r in ret:

    print(r.name);

for r in ret:

    print(r.name);

 

Django中iterator和exists

#使用exists(),查找记录是否存在,不会将数据查询到queryset

if ret.exists():

    print("存在记录")

#使用iterator(),当querySet很大时,避免内存占用

ret = Book.objects.all().iterator();

for r in ret:

    print(r.name);

#只能打印一遍

for r in ret:

    print(r.name);

 

Django的Model补充

        Modle的定义外键属性的参数。简单写法直接写表名,on_delete:删除时是否级联。也可以定义时,设置各种属性。

示例:

publish = models.ForeignKey(to="Publish",to_field="id",on_delete=models.CASCADE)

#to:设置外键表名

#to_field:设置外键关联栏位

#on_delete:设置删除时操作,

#models.CASCADE:级联删除;

#models.DO_NOTHING:删除时报错

#models.PROTECT:删除时保错

#models.SET_NULL:删除关联数据时,与之关联的变成NULL

#models.SET_DEFAULT:删除时,设置默认值

#models.SET:删除时,删除关联数据,设置成指定值,models.SET(指定值),或者models.SET(func())设置关联值为func()返回值。

#related_name:设置反向查找时使用的属性名,默认是表名_set代表对应表。

#related_query_name:设置反向查询的表名,表名__字段名

#limit_choices_to:筛选外键关联查询出的数据,只在admin中生效。例如:limit_choices_to={"id__gt" : 1},将id大于1的数据筛选出来,当admin添加数据时,设置关联字段值,会做筛选。

#db_constraint:False or True是否数据库中创建外键约束

#parent_link:False or True 在admin中是否显示关联数据

 

 

多对多关系建立额外属性设置:

使用ManyToManyField定义多对多关系

#进阶设置

author = models.ManyToManyField(

    #关联表

    to="Author",

    #是否创建外键约束

    db_constraint=True,

    #设置第三张表名

    db_table=None,

    #through,不自动创建第三张表,指定自己创建的Book_Author表

    through="Book_Author",

    through_fields=["book","author"]

)

 

db_table:设置第三张数据关系表表名,默认是两个关联表:表1_表2

        默认设置ManyToMany字段后,Django自动创建第三张关系表,但是这张表只能有三个字段,我们如果需要其他字段,需要自己建立第三张表。

示例:

class Book_Author(models.Model):

    book = models.ForeignKey("Book",on_delete=models.CASCADE);

    author = models.ForeignKey("Author",on_delete=models.CASCADE);

    #设置多个字段联合唯一,设置多个字段作为主键

    class Meta:

        unique_together=[

            ("book","author")

        ]

through:None,ManyToMany创建第三张表时,将第三张表指定为自己创建的表。这里可以指定through:Book_Author,

through_fields:None,指定关联字段,through_field:[“book“,“author”]

 

表的自关联

        多对多:表的ManyToManyField字段是自己本身。一对多也可以自关联,ForeignKeyField是字段本身。

示例:

class Book(models.Model):

    name = models.CharField(max_length=20,verbose_name="名称");

    price = models.FloatField();

    b = models.ManyToManyField("Book");

select_related函数:

Book表和Publish表是一对多关系,一本书对应一个出版社,一个出版社对应多本书。其中publish是Book表中外键。

查询关联字段,一次性查询出来,执行SQL语句

book_list = Book.object.all()

#这里通过对象获取Publish表数据,这里会执行SQL语句

#如果循环获取,会执行多次SQL,效能极差

book_list.publish.name

#使用select_related(外键栏位),一次性连接查询出来,可以传入多个外键栏位

book_list = Book.object.select_related(“publish”)

 

prefetch_related函数:

#使用prefetch_related(外键栏位),一次性连接查询出来,可以传入多个外键栏位

book_list = Book.object.prefetch_related(“publish”)

 

only函数和defer函数:

        使用only只取指定列,效果和values类似,但only返回结果QuerySet中依然是对象列表

        book_list = Book.object.only(“id”,”name”)

        使用defer不取指定列

        book_list = Book.object.defer(“id”)

 

using函数:

        通过using(“别名”),查询指定数据库。

在setting.py中,using(“default”)。如果有另外一个“default1”的数据库,我们就可以使用using(“default1”)查询另一个数据库。

DATABASES = {

    'default': {

        'ENGINE': 'django.db.backends.sqlite3',

        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),

    }

}

 

extra函数:

      #通过extra可以执行额外的子查询。

      Book.objects.extra(select = {“new_id”: “select id from Book where id > %s”},select_params = (10,))

      #where条件

      Book.objects.extra(where = [“name=’tang’”,”age > 10”])

 

raw函数:

   #使用raw函数直接执行sql语句,

   Book.objects.raw(“select id name from book”);

  # 使用參數为原生SQL赋值

   Book.objects.raw(“select * from book where id > %s”,params=[10,])

   #将获取的列名对应其他名

   name ={“id’:”ID”,”name”:”名称”,”price”:”价格”}

   Book.objects.raw(“select id name price from book”,translations=name)

   #使用数据库指定,使用默认default数据库

   Book.objects.raw(“select * from book”,using =”default”)

 

 

dates函数:

   获取时间栏位部分信息

   dates(self,field_name,kind,order=’ASC’)

   kind参数:year:年  month:年-月 day:年-月-日

   Date.objects.dates(“time”,year,”DESC”)

 

datetimes函数:

  获取时间栏位部分信息,年月日时分秒;

  Datetimes(self,field_name,kind,order,tz_info=None)

  Kind参数:year(年),month(月),day(日),hour(时),minute(分),second(秒)

  tzinfo:时区对象,pytz.timezone(“时区字符串”);

  使用pip3 install pytz

  import pytz

  #显示时区字符串

  pytz.all_timezones

 

none函数:

  获取空的QuerySet对象。

 

bulk_create函数:

  批量创建记录,

  bulk_create(self,objs,batch_size=None)

  objs:对象列表,批量创建记录

  batch_size:最大批量数

  objs[Book(name=”天下”),name=”传奇”]

  Book.objects.bulk_create(objs,10);

 

get_or_create函数:

  获取记录,如果没有创建后再获取。

obj,created = Book.objects.get_or_create(id=”2”,defaults={name=”test”,price=11})

 

update_or_create函数:

  更新记录,如果记录存在就根据defaults参数更新,不存在就创建。

  obj,created = Book.objects.update_or_create(id=”2”,defaults={name=”test”,price=11})

 

posted @ 2020-12-19 14:21  渔歌晚唱  阅读(183)  评论(0)    收藏  举报