python django框架ORM模型及ORM操作数据库 笔记

ORM模型介绍

随着项目的越来越大,采用写原生SQL的方式在代码中会出现大量的SQL语句,那么问题就出现了:
1、SQL语句重复利用率不高,越复杂的SQL语句条件越多,代码越长。会出现很多相近的SQL语句。
2、很多SQL语句是在业务逻辑中拼出来的,如果有数据库需要修改,就要去修改这些逻辑,这会很容易漏掉对某些SQL语句的修改。
3、写SQL语句时最容易忽略WEB安全问题,给未来造成隐患。例:SQL注入:

select * from user where username = 'root'#' and password = ''
select * from user where username = 'root'-- ' and password = ''
select * from sqldb_book where id = -1 or 1=1

ORM,全称Object Relational Mapping,中午叫做对象关系映射,通过ORM我们可以通过类的方式去操作数据库,而不用再写原生的SQL语句。通过把表映射成类,把行作实例,把字段作为属性,ORM在执行对象操作的时候最终还是会把对应的操作转换为数据库原生语句。
在这里插入图片描述

使用ORM的优点

1、易用性:使用ORM做数据库的开发可以有效地减少重复SQL语句的概率,写出来的模型也更加直观清晰。
2、性能损耗小:ORM转换成底层数据库操作指令确实会有一些性能消耗,但从实际情况来看,这种性能损耗很少(不足5%),只要不是对性能有严苛的要求,综合考虑开放效率,代码的阅读性,带来的好处要远远大于性能损耗,而且项目越大作用越明显。
3、设计灵活:可以轻松的写出复杂的查询。
4、可移植性:Django封装了底层的数据库实现,支持多个关系数据库引擎,包括流行的MySQL,PostgreSQL和SQLite。可以非常轻松的切换数据库

创建ORM模型

ORM模型一般都是放在app的model.py文件中。每个app都可以拥有自己的模型。并且如果这个模型想要映射到数据库中,那么这个app必须要放在settings.py的INSTALLED_APP中进行安装。一下是写一个简单的书籍ORM模型。
在这里插入图片描述
以上便定义了一个模型。这个模型继承自django.db.models.Model,如果这个模型想要映射到数据库中,就必须继承自这个类。这个模型以后映射到数据库中,表名是模型名称的小写形式,为book,在这个表中,有四个字段,一个为name,这个字段是保存的书的名称,同样也是varchar类型,最长不能超过20个字符。第二个字段是作者名字类型,同样也是varchar类型,最长不能超过20个字符,第三个是出版时间,数据类型是datetime类型,默认是保持这本书籍的时间。第五个是这本书的价格,是浮点价格。
还有一个字段我们没有写,就是主键id,在django中,如果一个模型没有定义主键,那么将会生成一个自动增长的int类型的主键,并且这个主键的名字就叫做id。

映射模型到数据库中

将ORM模型映射到数据库中,总结起来就是以下几步:
1、在settings.py中,配置好DATABASE,做好数据库相关的配置。
2、在app中的models.py中定义好模型,这个模型必须继承自django.db.models。
3、将这个app添加到settings.py的INSTALLED_APP中。
4、在命令行终端,进入到项目所在路径,然后执行命令python manage.py makemigrations来生成迁移脚本文件。
5、同样在命令行中,执行命令python manage.py migrate来迁移脚本文件映射到数据库中。
如果没有指定主键,django会自动生成一个id主键,映射的表名为appname_classname。
在这里插入图片描述
在这里插入图片描述

ORM的增删改查

1、添加数据
在这里插入图片描述

2、查询数据
(1)根据主键进行查找

book=Book.objects.get(pk=1)
print(book)

(2)根据其他条件来查找

book=Book.objects.filter(name='Python')
print(book)

(3)查询所有

book=Book.objects.all()

3、删除数据

book=Book.objects.get(pk=1)
book.delete()

3、修改数据

book=Book.objects.get(pk=1)
book.price=200
book.save()

示例:
在这里插入图片描述

常用字段

AutoField

映射到数据库中是int类型,可以有自动增长的特性。一般不需要使用这个类型,如果不指定主键,那么模型会自动的生成一个叫做id的自动增长的主键。如果你想指定一个其他名字的并且具有自动增长的主键,使用AutoField也是可以的。

BigAutoField

64位的整形,类似于AutoField,只不过是产生的数据的范围是从1-9223372036854775807。

BooleanField

在模型层面接收的是True/False。在数据库层面是tinyint类型。如果没有指定默认值,默认值是None。

CharField

在数据库层面是varchar类型。在Python层面就是普通的字符串,这个类型在使用的时候必须要指定最大的长度,也必须要传递max_length这个关键字参数进去。

DateTimeField

日期时间类型,不仅仅可以存储日期,还可以存储时间,映射到数据库中是datetime类型。

TIME_ZONE='Asia/Shanghai'
from django.utils.timezone import localtime,now

1、auto_now:在每次这个数据保存的时候,都使用当前的时间。比如作为一个记录修改日期的字段,可以将这个属性设置为True。

EmailField

类似于CharField。在数据库底层也是一个varchar类型。最大长度是254个字符。

FileField

用来存储文件的

ImageField

用来存储图片的

BigIntegerField

大整形,值的区间是-9223372036854775808~9223372036854775807

PositiveIntegerField

正小整形,值的区间是0-214783647

SmallIntegerField

小整形,值的区间-32768-32767

PositiveSmallIntegerField

正小整形,值的区间是0-32767

TextField

大量的文本类型,映射到数据库中是longtext类型

UUIDField

只能存储uuid格式的字符串,uuid是一个32位的全球唯一的字符串,一般用来作为主键

URLField

类似于CharField,只不过只能用来存储url格式的字符串,并且默认的max——length是200

Field的常用参数

1、null
如果设置为True,Django将会在映射表的时候指定是否为空,默认是False。在使用字符串相关的Field(CharField/TextField)的时候,官方推荐尽量不要使用这个参数,也就是保持默认值False。因为Django在处理字符串相关的Field的时候,即使这个Field的null=False,如果你没有给这个Field传递任何值,那么Django也会使用一个空的字符串’'来作为默认值存储进去,因此如果再使用null=True,Django会产生两种空值的情形(NULL或者空字符串)。如果想要在表单验证的时候允许这个字符串为空,那么建议使用blank=True,如果你的Field是BooleanField,那么对应的可空的字段则为NullBooleanField。
2、db_column
这个字段在数据库中的名字,如果没有设置这个参数,那么将会使用模型中属性的名字
3、default
默认值,可以为一个值,或者是一个函数,但是不支持lambda表达式,并且不支持列表/字典/集合等可变的数据结构。
4、primary_key
是否为主键,默认是False
5、unique
在表中这个字段的值是否唯一,一般是设置手机号码/邮箱等。

模型中Meta配置

对于一些模型级别的配置,我们可以在模型中定义一个类,叫做Meta。然后在这个类中添加一些类属性来控制模型的作用,比如我们想要在数据库映射的时候使用自己指定的表名,而不是使用模型的名称,那么我们可以在Meta类中添加一个db_table的属性

class Meta:
		db_table = 'users'
		

在类中定义,即可更改数据表名称

db_table
这个模型映射带数据库中的表名,如果没有这个指定的参数,那么在映射的时候将会使用模型名来作为默认的表名。
ordering
设置在提取数据的排序方式,比如我想在查找数据的时候根据添加的时间排序

class Meta:
		db_table = 'users'
		#根据uid正序打印
		ordering=['uid']
		#根据uid倒序打印
		ordering=['-uid']
		

外键和表关系

在MySQL中,表有两种引擎,一种是InnoDB,另一种是myisam。如果使用的是InnoDB引擎,是支持外键约束的,外键的存在使得ORM框架在处理表关系的时候异常的强大,因此这里我们首先来介绍下外键在Django中的使用。

类定义为class ForeignKey(to,on,delete,**options),第一个参数引用的是哪个模型,第二个参数实在使用外键引用的模型数据被删除了,这个字段该如何处理,比如有CASCADE、SET_NULL等,这里以一个实际案例来说明,比如有一个Categoru和一个Article两个模型。一个Category可以有多个文章,一个Article只能有一个Category,并且通过外键进行引用。

class Category(models.Model):
		name = models.ChaarField(max_length=100)

class Article(models.Model):
		title = models.CharField(max_length=100)
		content = models.TextFiels()
		category = model.ForeignKey('Category',on_delete=models.CASCADE) 

映射好表和外键后,进行保存操作

def news(request):
	category = Category(name='python')
	category.save()
	article = Article(title='Python入门‘,content='***')
	article.save()
	return HttpResponse("news index")

根据外键直接查找文章分类

article = Article.objects.get(pk=1)
article.category.name

以上使用ForeignKey来定义模型之间的关系,即在article的实例中可以通过author属性来操作对应的User模型,这样使用起来非常的方便。

为什么使用了ForeignKey后,就能通过author访问到对应的user对象呢,因为在底层,Django为Article表添加了一个属性名_id的字段(比如author的字段名称是author_id),
这个字段是一个外键,记录着对应的作者的主键,以后article.author访问的时候,实际上是先通过author_id找到对应的数据,然后再提取User表中的这条数据,形成一个模型。
如果想要引用另外一个app的模型,那么应该在传递to参数的时候,使用app.model_name进行指定,以上例为例,如果User和Article不是在同一app中

#User模型在user这个app中
class User(models.Model):
		username = models.CharField(max_lebgth=20)
		password = models.CharField(max_lebgth=100)

#Article模型在article这个app中
class Article(models.Model):
		title = models.CharField(max_lengt=100)
		content = models.TextField()

		author = models.ForeignKey("user.User",on_delete=models.CASCADE)

如果外键引用自身,那么to参数用self或者自身模型名,在论坛中,一般评论都可以进行二级评论,即可以针对另外一个评论进行评论,那么在定义模型的时候就需要使用外键来引用自身。

外键的删除操作

如果一个模型使用了外键,那么在对方哪个模型被删除后,该进行什么样的操作,可以通过on_delete未指定,
1、CASCADE:级联操作,如果外键对应的那条数据被删除了,那么这条数据也会被删除。
2、PROTECT:受保护,即只要这条数据引用了外键的那条数据,那么就不能删除外键的那条数据。
3、SET_NULL:设置为空,如果外间的那条数据被删除了,那么在本条数据上就将这个字段设置为空
4、SET_DEFAULT:设置默认值,如果外键的那条数据被删除了,那么本条数据上将这个字段设置为默认值
5、SET():如果外键的那条数据被删除了,那么将会获取SET函数中的值来作为这个外键的值
6、DO_NOTHING:不采取任何行为,一切全看数据库级别的约束。

查询操作

查找是数据库操作中一个非常重要的技术,查询一般就是使用filter,exclude以及get三个方法来实现。我们可以在调用这些方法的时候传递不同的参数来实现查询需求。在ORM层面,这些查询条件都是使用field+_+condition的方式来实现的。

查询条件

查询时用filter可以查询空值,用get查询空值则报错,写代码时尽量使用filter

以下测试代码在创建模型后才能使用,以下为模板源码示例:

from django.db import models
class Category(models.Model):
		name = models.CharField(max_length=100)
class Article(models.Model):
		title = models.CarField(max_length=200)
		content = models.TextField()
		category = models.ForeignKey("Category",on_delete=SET_NULL,null=True)
		create_time = models.DateTimeField(auto_now_add=True)
		def __str__(self):
			return "Article:title:%s,content:%s"%(self.title,self.content)
exact

使用精确的=进行查找,如果提供的是一个None,那么SQL层面就是被解释为NULL

article = Article.objects.filter(id__exact=14)
article = Article.objects.filter(id__exact=None)

以上的两个查找翻译为SQL语句

select * from article where id=14;
select * from article where id is NULL;

article query可以得到Django执行的SQL语句,但是只能作用于QuerySet对象上。

iexact

使用like进行查找

article = Article.objects.filter(title_iexact='hello word')

以上查询就等价于以下SQL语句:
select * from article where title like 'hello word'

注意上面这个sql语句,因为在MySQL中,没有一个叫做like的,所以exact和iexact的区别实际上就是LIKE和=的区别,在大部分collation=utf8_general_ci情况下都是一样的(collation是用来对字符串对比的。)

contains

大小写敏感,判断某个字段是否包含了某个数据。

articles = Article.objects.filter(title__contains='hello')
#翻译成SQL语句为如下:
select * where title like binary '%hello%';

要注意的是,在使用contains的时候,翻译成sql语句左右两边是有百分号的,意味着使用的是模糊查询,而exact翻译成sql语句左右两边是没有百分号的,意味着试用的是精确查询

icontains

大小写不敏感的匹配查询(模糊查询)

articles = Article.objects.filter(title__icontains='hello')
#
select * where title like '%hello%';
in

提取那些给定的field的值,是否在给定的容器中,容器可以为list、tuple或者任何一个可以迭代的对象,包括QuerySet对象。

articles = Article.objects.filter(id__in=[1,2,3])
#以上代码在翻译成SQL语句为以下:
select * from articles where id in (1,2,3)
#当然也可以传递一个QuerySet对象进去
#查找标题为hello的文章分类
articles = Article.objects.filter(title__icontains='hello')
category = Category.objects.filter(article__in=articles)
#查找文章ID为1,2,3的文章分类
category = Category.objects.filter(article_id_in=[1,2,3]);
根据关联的表进行查询
想要获取文章标题中包含"hello"的所有的分类,那么可以通过以下代码来获取:
categories = Category.objects.filter(article_title_icontains='hello');

比较运算

gt

某个field的值要大于给定的值

#将所有id大于4的文章全部加载出来
articles = Article.objects.filter(id__gt=4)
#将翻译成以下sql语句:
select * from articles where id > 4;

gte

类似于gt,是大于等于。

lt

类似于gt是小于

lte

类似于lt,是小于等于。

range

判断某个field的值是否在给定的区间中。

start_date = datetime(year=2010,month=1,day=1,hour=10,minute=0,second=0)
end_date = datetime(year=2020,month=1,day=30,hour=10,minute=0,second=0)

date_range=Commor.objects.filter(test_date_range=(start_date,end_date))

#以上代码的意思是提取所有发布的时间是在2010/1/1到2020/1/30之间的文章,翻译为SQL语句为:
select 'user_common'.'id','user_common'.'content','user_common'.'pid','user_common'.'test_date' from 'user_common' where 'user_common'.'test_date' between 2010-01-01 10:00:00 and 2020-01-30 10:00:00;

date

针对某些date或者datetime类型的字段,可以指定date的范围,并且这个时间过滤,还可以使用链式调用。

date_test =Common.objects.filter(test_date__date=datetime(year=2018,month=12,day=19))

print(date_test.query)
print(date_test)
#翻译为SQL语句为:
select `article_article`.`id`,`article_article`.`title`,`article_article`.	`content`,`article_article`.`category_id`,`article_article`.`create_time` from `article_article` where date(`article_article`.`test_date`) > 2018-12-19

year

根据年份查找

article = Article.objects.filter(create_time__year=2020)  #查询2020年的数据
article = Article.objects.filter(create_time__year__gt=2019)#查询大于2020年的数据
article = Article.objects.filter(create_time__date__gt=datetime(year=2020,month=7,day=7))

time

#查询特定时间的记录
article = Article.objects.filter(create_time__time=time(hour=22,minute=11,second=37))
#查询某个时间段的记录
start_time=time(hour=17,minute=21,second=11)
end_time=time(hour=17,minute=25,second=11)
date_test = Article.objects.filter(create_time__time__range=(start_time,end_time))

聚合函数

如果你使用原生SQL,则可以使用聚合函数来提取数据,比如提取某个商品销售的数量,那么可以使用Count,如果想要知道商品销售的平均价格,那么可以使用Avg。
聚合函数就是通过aggregate方法来实现的

例:
提前准备好的模型:

from django.db import models

	class Author(models.Models):
	#作者模型
	name = models.CharField(max_length=100)
	age = models.InterField()
	email = models.EmailField()
	class MJeta:
		db_table = 'author'

	class Publisher(models.Models):
	#出版社模型
	name = models.CharField(max_length=300)
	
	class MJeta:
		db_table = 'publisher'

	class Bookr(models.Models):
	#图书模型
	name = models.CharField(max_length=300)
	pages = models.InterField()
	price =models.FloatField()
	rating = models.FloatField()
	author = models.Foreignkey(Author,on_delete = models.CASCADE)
	publisher = models.Foreignkey(Publisher,on_delete=models.CASCADE)
	class MJeta:
		db_table = 'book'

	class BookOrder(models.Models):
	#图书订单模型
	book = models.Foreignkey('Book',on_delete=models.CASCADE)
	price = models.FloatField()
	class MJeta:
		db_table = 'book_order'

聚合函数的使用

1、Avg:求平均值。比如想要获取所有图书的价格平均值,那么可以使用一下代码实现

from django.db.models import Avg
from django.db import connection
result = Book.objects.aggregate(Avg('price'))
print(result)
#以上的打印结果:
{"price_avg":23.0}
其中price_avg的结构是根据field_avg规则构成的,如果想要修改默认的名字,那么可以将Avg赋值给一个关键字
result = Book.objects.aggregate(my_avg=Avg('price'))
print(result)
那么以上的结果打印为:
{"my_avg":23}

2、Count:获取指定的对象的个数

from django.db.models import Count
result = Book.objects.aggregate(book_num=Count("id"))
以上的result将返回Book表中总共有多少本图书
Count类中,还有另外一个参数叫做distinct,默认是等于False,如果是等于True,那么将去掉那些重复的值。
比如要获取作者表中所有的不重复的邮箱有多少个

from django.db,models import Count
result =Author.objects.aggregate(count=Count("email",distinct=True))

#统计每本图书的销量
result =Book.objects.annotate(book_nums=Count("bookorder"))
for book in result:
	print("%s%s"%(book.name,book.nums))

3、Max和Min:获取指定对象的最大值和最小值,比如想要获取Autor表中,最大的年龄和最小的年龄分别是多少。

from django.db.models import Max.Min
result =Author.objects.aggregate(Max('age'),Min('age'))
#如果最大年龄为88,最小年龄为18,,那么以上的result将为:
{"age__max":88,"age__min":18}

#统计每本售卖图书的最大值和最小值
request=Book.objects.annote(max=Max("bookorder__price"),min=Min("bookorder__price"))
print(request)

4、Sum:求指定对象的总和,比如要求图书的销售总额。

	from django.db.models import Sum
	result =Book.objects.aggregate(total=Sum("price"))
	#每一本图书的销售总额
	result =Book.objects.annotate(total=Sum("bookorder__price"))
	#统计2019年,销售总额
	result = BookOrder.objects.filter(create_time__year=2019).aggregate(total=Sum("price"))

aggregate和annotate的区别

1、aggregate:返回使用聚合函数后的字段和值
2、annotate:在原来模型字段的基础上添加一个使用了聚合函数的字段,并且在使用聚合函数的时候,会使用当前这个模型的主键进行分组(group by)

#求每一本图书销售的平均价值
result =Book.objects.aggregate(avg=Avg("bookorder__price"))
print(result)
print(connect.queries)

result =Book.objects.annotate(avg=Avg("bookorder__price"))
print(result)
print(connect.queries)

F表达式和Q表达式

F表达式

F表达式是用来优化ORM操作数据库的,比如我们要将公司所有员工的薪水都增加1000元,如果按照正常的流程,应该是先从数据库中提取所有员工工资到Python内存中,然后使用Python代码在员工工资基础上增加1000元,最后再保存到数据库中,这里面涉及的流程就是,首先从数据库中提取数据到Python内存中,然后再Python内存中做完运算后再保存到数据库中。

employees = employee.objects.all()
for employee in employees:
	employee.salary += 1000
	employee.save()

而我们的F表达式就可以优化这个流程,他可以不需要先把数据从数据库中提取出来,计算完成后再保存回去,他可以直接执行sql语句,就将员工的工资增加1000元。

from django.db.models import F
Employee.objects.update("salary"+1000)
#表达式并不会马上从数据库中获取数据,而是生成sql语句的时候,动态的获取传给F表达式的值

比如如果想要获取作者中,name和email相同的作者数据,如果不使用F表达式。

authors = Author.objects.all()
for author in authors:
	if author.name == author.email:
		print(author)
#如果用F表达式,那么一行代码就可以确定,示例:
from django.db.models import F
authors = Author.objects.filter(name=F("email"))

Q表达式

如果想要实现所有价格高于100元,并且评分达到9.0以上评分的图书。

books = Book.objects.filter(price__get=100,rating__gte=9)

以上这个案例是一个并集查询,可以简单的通过传递多个条件进去来实现,
但是如果想要实现一些复杂的查询语句,比如要查询所有价格低于10元,或者是评分低于9分的图书,那么就没有办法通过传递多个条件进去实现了,这时候就需要使用Q表达式来实现了。

from django.db.models import Q
books = Book.objects.filter(Q(price__lte=10) | Q(rating__lte=9) )

以上是进行或运算,当然还可以进行其他的运算,比如&和~(非)等。

from django.db.models import Q
#获取id等于3的图书
books = Book.objects.filter(Q(id=3))
#获取id等于3,或者名字中包含中文“传”的图书
books = Book.objects.filter(Q(id=3) | Q(name__contains="传"))
#获取价格大于100,并且书名中包含"传"的图书
books = Book.objects.filter(Q(price__gte=100) & Q(name__contains="传"))
#获取书名中包含“转”,但是id不等于3的图书
books = Book.objects.filter(Q(name__contains="传") & ~Q(id=3))

QuerySet的方法

QuerySet API

我们通常做查询操作的时候,都是通过模型名字.objects的方式进行操作,其实模型名字.objects是django.db.models.manager.Manager对象,而Manager这个类是一个"空壳"的类,他本身是没有任何的属性和方法的,他的方法全部都是通过Python动态添加的方式,从QuerySet类中拷贝过来的。
type不仅能查看数据类型,还能创建类,与class相似,使用方法type(name,bases,dict),其中name为类的名字,bases为继承的父类,dict为可传入的参数。

在这里插入图片描述

QuerySet的方法

在使用QuerySet进行查找查询的时候,可以提供多种操作,比如过滤完后还要根据某个字段进行排序,那么之一系列操作我们可以通过一个非常流畅的链式调用的方式进行,比如要从文章表中获取标题为123,并且提取后要将结果根据发布的时间进行排序,那么可以使用一下方式来完成

articles = Article.objects.filter(title="123").order_by("create_time")

可以看到order_by方法是直接在filter执行后调用的,这说明filter返回的对象是一个拥有order_by方法的对象,而这个对象正是一个新的QuerySet对象,因此可以使用order_by方法。

那么以下将介绍在那些会返回新的QuerySet对象的方法:
1、filter:将满足条件的数据提取出来,返回一个新的QuerySet
2、exclude:排除满足条件的数据,返回一个新的QuerySet

#提取那些标题不包含"hello"的图书
Article.objects.exclude(title__contains='hello')

3、annotate:给QuerySet中每个对象都添加一个使用查询表达式(聚合函数、F表达式、Q表达式、Func表达式等)的新字段

#将在每个对象中都添加一个"author__name"的字段,用来显示这个文章的作者的年龄
articles = Article.objects.annotate(author_name=F("author__name"))

4、order_by:指定将查询的结果根据某个字段进行排序,如果要倒序排序,那么可以在这个字段的前面加一个负号

#根据创建的时间正序排序(从小到大,默认排序)
articles = Article.objects.order_by("create_time")
#根据创建的时间倒序排序
articles = Article.objects.order_by("-create_time")
#根据作者的名字进行排序
articles = Article.objects.order_by("author__name")
#首先根据创建的时间进行排序,如果时间相同,则根据作者的名字进行排序
articles = Article.objects.order_by("create_time","author__name")
#根据图书订单的评分来排序
articles = Article.objects.order_by("cbook__rating")

5、values:用来指定在提取数据出来,需要提取哪些字段,默认情况下会把表中所有的字段全部提取出来,可以使用values来指定,并且使用了values方法后,提取出的QuerySet中的数据类型不是模型,而是在values方法中指定的字段和值形成的字典

articles = Article.objects.values("title","content")
for article in articles:
		print(article)

以上打印出来的article是类似于{“title”:“abc”,“content”:“xxx”}的形式

6、values_list:类似于values,只不过返回的QuerySet中,存储的不是字典,而是元祖

articles = Article.objects.values_list("id","title")
print(articles)

那么在打印articles后,结果为<QuerySet [(1,“abc”),(2,“xxx”)]>等

7、all:获取这个ORM模型的QuerySet对象
8、select_related:在提取某个模型数据的同时,也提前将相关的数据提取出来,比如提取文章数据,可以使用select_related将author信息提取出来,以后再次使用article.author的时候就不需要再次去访问数据库了,可以减少数据库查询的次数

article = Article.objects.get(pk=1)
>>article.author   #重新执行一次查询语句
article = Article.objects.select_related("author").get(pk=2)
>>article.author  #不需要重新执行查询语句了

selected_related只能用在一对多或者一对一中,不能用在多对多或多对一中,比如可以提前获取文章的作者,但是不能通过作者获取这个作者的文章,或者是通过某篇文章获取这个文章所有的标签
9、prefetch_related:这个方法和selected_related非常的相似,就是在访问多个表中的数据的时候,减少查询的次数,这个方法是为了解决多对一和多对多的关系的查询问题,比如要获取标题中带有hello字符串的文章以及他的所有标签

from django.db import connection
articles = Article.objects.prefetch_related("tag_set").filter(title__contains='hello')
print(articles.query)
for article in articles:
		print("title:",article.title)
		print(article.tag_set.all())

10、create:创建一条数据,并且保存到数据库中,这个方法相当于先用指定的模型创建一个对象,然后再调用这个对象的save方法

article = Article(title="abc")
article.save()
#下面这行代码相当于以上两行代码
article = Article.objects.create(title="abc")

11、get_or_create:根据某个条件进行查找,如果找到了那么就返回这条数据,如果没有查找的,那么就创建一个

obj,created = Category.objects.get_or_create(title="默认分类")

如果有标题等于默认分类的分类,那么就会查找出来,如果没有,则会创建并且存储到数据库中。
这个方法的返回值是一个元祖,元祖的第一个参数obj是这个对象,第二个参数created代表是否创建

12、exists:判断某个条件的数据是否存在,如果要判断某个条件的元素是否存在,那么建议使用exists,这比使用count或者直接判断QuerySet更有效的多

if Article.objects.filter(title__contains="hello").exists():
	print(Ture)
#比使用count更高效:
if Article.objects.filter(title__contains="hello").count() > 0:
	print(Ture)
#也比直接判断QuerySet更高效:
if Article.objects.filter(title__contains="hello"):
	print(Ture)

13、update:执行更新操作,载SQL底层走的也是update命令,比如要将所有category为空的article的article字段都更新为默认的分类

Article.objects.filter(category__isnull=True).update(category_id=3)

注意这个方法走的是更新的逻辑,所以更新完成后保存到数据库中不会执行save方法,因此不会更新auto_now设置的字段

14、切片操作:有时候我们查找数据,有可能只需要其中的一部分

`books = Book.objects.all():[1:3]
for book in books:
	print(book)

切片操作并不是把所有数据从数据库中提取出来再做切片操作,而是在数据库层面使用LIMIE和OFFSET来帮我们完成,所以如果只需要取其中一部分的数据的时候,建议大家使用切片操作。

将QuerySet转换为SQL去执行

生成一个QuerySet对象并不会马上转换为SQL语句去执行

from django.db import connection
books = Book.objects.all()
print(connection.queries)

我们可以看到在打印connection.queries的时候打印的是一个空的列表,说明上面的QuerySet并没有真正的执行。
在以下情况中QuerySet会被转换为SQL语句执行

1、迭代:在遍历QuerySet对象的时候,会首先执行这个SQL语句,然后再把这个结果返回进行迭代,比如以下代码就会转换为SQl语句:

for book in Book.objects.all():
	print(book)

2、使用步长做切片操作:QuerySet可以类似于列表一样做切片操作,做切片操作本身不会执行SQl语句,但是如果在做切片的时候提供了步长,那么就会立马执行SQL语句,需要注意的是,做切片后不能再执行filter方法,否则会报错
3、调用len函数:调用len函数用来获取QuerySet中总共有多少条数据也会执行SQL语句
4、调用list函数:调用list函数用来将一个QuerySet对象转换为list对象也会立马执行SQl语句
5、判断:如果对某个QuerySet进行判断,也会立马执行SQL语句

ORM模型迁移

迁移命令

1、makemigrations:将模型生成迁移脚本,模型所在的挨批评、,必须放在settings.py中的INSTALLED_APPS中。这个命令有以下几个常用选项:
·app_label:后面可以跟一个人或多个app,那么就只会针对这几个app生成迁移脚本。如果没有任何的app_label,那么会检查INSTALLED_APPS中所有的app下的模型,针对每一个app都生成响应的迁移脚本
·–name:给这个迁移脚本指定一个名字
·–empty:生成一个空的迁移脚本,如果你想写自己的迁移脚本,可以使用这个命令来实现一个空的文件,然后再在文件中写迁移脚本

2、migrate:将新生成的迁移脚本,映射到数据库中,创建新的表或者修改表的结构,以下一些常用的选项:
·app_label:将某个app下的迁移脚本映射到数据库中,如果没有指定,那么会将所有在INSTALLED_APPS中的app下的模型都映射到数据库中。
·app_label migrationname:将某个app下指定名字的migration文件映射到数据库中。
·–fake:可以将指定的迁移脚本名字添加到数据库中,但是并不会把迁移脚本转换为SQL语句,修改数据库中的表
·–fake-initial:将第一次生成的迁移文件版本号记录在数据库中,但并不会真正的执行迁移脚本

3、showmigrations:查看某个app下的迁移文件,如果后面没有app,那么将查看INSTALLED_APPS中所有的迁移文件
python manage.py showmigrations [app名字]`

4、sqlmigrate:查看某个迁移文件在映射数据库的时候,转换的sql语句

python manage.py sqlmigrate book 0001_initial

migrations中的迁移版本和数据库中的迁移版本对不上怎么办?

1、找到哪里不一致,然后使用python manage.py --fake [版本名字],将这个版本标记为已经映射。
2、删除指定app下migrations和数据库表django_migrations中和这个app相关的版本号,然后看,将模型中的字段和数据库中的字段保持一致,再使用命令python manage.py migrate --fake-initial来将这个初始化的迁移脚本标记为已经映射,以后再修改就没有问题了。

根据已有的表自动生成模型

在实际开发中,有些时候可能数据库已经存在了,如果我们用Django来开发一个网站,读取的是之前已经存在的数据库中的数据。那么该如何将模型与数据库中的表映射呢?根据旧的数据库生成对应的ORM模型,需要以下几个步骤:
1、Django给我们提供了一个inspectdb的命令,可以非常方便的将已经存在的表,自动的生成模型,想要使用inspectdb自动将表生成模型,首先需要在settings.py中配置好数据库相关信息,不然找不到数据库

DATABASES = {
    'default': {
        #引擎
        'ENGINE': 'django.db.backends.mysql',
        #数据库的名字
        'NAME': 'django_db',
        #用户名
        'USER':'root',
        #密码
        'PASSWORD':'root',
        #主机地址
        'HOST':'127.0.0.1',
        #端口号
        'port':'3306'
    }
}
#执行下面命令保存到models.py里面
python manage.py inspectdb > models.py

2、修正模型:新生成的ORM模型可能有些地方不太合适,比如模型的名字,表之间的关系等等

(1)模型名:自动生成的模型,是根据表的名字生成的,可能不是你想要的,这时候模型的名字你可以改成任何你想要的
(2)模型所属app:根据自己的需要,将相应的模型放在对应的app中,放在同一个app中也是没有任何问题的,只是不方便管理
(3)模型外键引用:将所有使用ForeignKey的地方,模型引用都改成字符串,这样不会产生模型顺序问题,另外,如果引用的模型已经移动到其他app中了,那么还要加上这个app的前缀
(4)让Django管理模型:将Meta下的managed=False删掉,如果保留这个,那么以后这个模型有任何的修改,使用migrate都不会映射到数据库中
(5)当有多对多的时候,应该也要修正模型,将中间表注释了,然后使用ManyToManyField来实现多对多,并且,使用ManyToManyField生成的中间表的名字可能和数据库中那个中间表的名字不一致,这时候肯定就不能正常连接了,那么可以通过db_table来指定中间表的名字。

class Article(models.Model):
		title = models.CharField(max_length=100,blank=True,null=True)
		content = models.TextField(blank=True,null=True)
		author = models.ForeignKey("front.User",models.SET_NULL,blank=True,null=True)
		tags = models.ManyToManyField("Tag",db_table="article_tag")
		class Meta:
			db_table = "arricle"

(6)表名:切记不要修改表的名字,不然映射到数据库中,会发生找不到对应表的错误

3、执行命令python manage.py makemigrations生成初始化的迁移脚本,方便后面通过ORM来管理表。这时候还需要执行命令python manage.py migrate --fake-initial,因为如果不使用–fake-initial,那么会将迁移脚本映射到数据库中,这时候迁移脚本会创建新的数据表,而这个表是已经存在的,所以肯定会报错,此时我们只需要将这个0001-initial的状态修改为已经映射,而不是真正执行映射,下次再migrate的时候,就会忽略他。

4、将Django的核心表映射到数据库中:Django中还有一些核心的表也是需要创建的,不然有些功能是用不了的,比如auth相关表,如果这个数据库之前就是使用Django开发的,那么这些表就已经存在了,可以不用管了,如果之前这个数据库不是使用Django开发的,那么应该使用migrate命令将Django中的核心模型映射到数据库中。

  • 1
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值