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

2023-10-26

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中的核心模型映射到数据库中。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

python django框架ORM模型及ORM操作数据库 笔记 的相关文章

  • 提交搜索表单后无法使用 Flask 应用程序从 url 检索变量

    我想在用户提交搜索表单后呈现一个新视图 我的制作方式与处理其他视图的方式相同 但不幸的是这次没有发生任何事情 我无法从应用程序路由中检索内容 所以这个问题不是重复的this https stackoverflow com questions
  • Django Rest Framework 嵌套序列化器不显示相关数据

    我使用 Django Rest Framework 进行了基本设置 我有两个模型和一个嵌套序列化器设置 models py from django db import models class Plan models Model name
  • pip 中的新彩色终端进度条

    我发现新版本的pip Python的包安装程序 有一个彩色进度条来显示下载进度 我怎样才能做到这一点 Like this pip 本身正在使用rich https pypi org project rich 包裹 特别是 他们的进度条文档
  • 在 ReportLab 中向画布元素添加超链接的最简单方法是什么?

    我正在使用 ReportLab 使用 Python 制作 pdf 我想向画布添加一个形状 并让该形状充当超链接 使以下示例中的矩形链接到 google com 的最简单方法是什么 from reportlab pdfgen import c
  • 用户在对话框中输入

    python 中是否有任何库可用于图形用户输入 我知道关于tk但我相信需要一些代码才能做到这一点 我正在寻找最短的解决方案 a input Enter your string here 取而代之的是 我想要一个对话框 以便用户可以在那里输入
  • Spark MLLib 存在问题,导致概率和预测对于所有内容都相同

    我正在学习如何将机器学习与 Spark MLLib 结合使用 目的是对推文进行情感分析 我从这里得到了一个情感分析数据集 http thinknook com wp content uploads 2012 09 Sentiment Ana
  • python TypeError:“NoneType”对象没有属性“__getitem__”

    这次我尝试另一个例子索莱姆的博客 http www janeriksolem net 2012 08 reading gauges detecting lines and html 它是一个使用霍夫变换检测图像中的直线和圆的模块 这是代码
  • 如何为Python的mechanize设置超时值?

    如何为Python的mechanize设置超时值 亚历克斯是正确的 mechanize urlopen需要一个timeout争论 因此 只需插入一些浮点型秒数 http docs python org library socket html
  • pip 安装与本地包具有相同命名空间的包

    我使用的是 Python 3 6 5 通过 miniconda 安装 我的问题是由于我正在安装一个与本地包具有相同命名空间的包 pip 安装此包后 我无法再从本地包导入 我收到一个ModuleNotFoundError错误 如果可能的话 命
  • 如何更新 certifi 的根证书?

    我正在使用 certifi python 模块来验证 ssl 连接 我查看了 certifi python2 7 site packages certifi cacert pem 中包含的根证书 其中一些证书已过期 我如何更新这些证书 我尝
  • Numpy - 两个矩阵的行之间的协方差

    我需要计算两个不同矩阵的每一行之间的协方差 即第一个矩阵的第一行与第二个矩阵的第一行之间的协方差 依此类推 直到两个矩阵的最后一行 我可以在没有 NumPy 的情况下使用下面附加的代码来完成此操作 我的问题是 是否可以避免使用 for 循环
  • 如何在 Python 中将列表变量传递给 subprocess.call 命令

    我有一个清单 apps apps append wq35a5huqlja45jsyukrpmwuiayovrmh apps append q7mimvgduueernwvw4y22t5huemykntw apps append pmudbp
  • 如果 Django 中的表单字段与 Python 关键字同名,如何声明该字段?

    我在 Django 中有一个简单的表单 看起来像这样 class SearchForm forms Form text forms CharField from forms DateField until forms DateField 失
  • 在 python matplotlib 中格式化损坏的 y 轴

    我正在 matplotlib 中处理一个 相当复杂的 条形图 它包含来自多个源的摘要数据 每个源都沿 x 轴标记 y 轴上有一系列结果 许多结果都是异常值 我尝试使用断开的 y 轴来显示这些结果 而不会使用以下组合来扭曲整个图表这个方法 h
  • Python 中字典的 enumerate()

    我知道我们用enumerate用于迭代列表 但我在字典上尝试过 但没有给出错误 CODE enumm 0 1 1 2 2 3 4 4 5 5 6 6 7 7 for i key in enumerate enumm print i key
  • 导入后属性未添加到模块中

    我做了以下实验室 vagrant ubuntu xenial test tree pack1 init py mod1 py pack2 init py mod2 py mod3 py test py 2 directories 6 fil
  • 字典条目被覆盖? [复制]

    这个问题在这里已经有答案了 我发现一些输入没有存储在 Python 3 的字典中 运行这段代码 N int input How many lines of subsequent input graph for n in range N st
  • 为什么这个多处理代码会失败? [复制]

    这个问题在这里已经有答案了 def sample pass Process target sample start Process target sample start 上面的代码失败并出现错误 已尝试在当前进程之前启动新进程 进程已完成
  • Python TDD 目录结构

    Python 中是否有用于 TDD 的特定目录结构 教程讨论测试的内容 但不讨论测试的位置 通过研究 Python Koans 怀疑它是这样的 project main program py This has main method sta
  • 用 Ruby 或 Python 解析 SVG 的库 [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 SVG 是一个庞大的标准 它基于 XML 我过去曾将 SVG 解析为 XML 然而 有些事情很难 例如

随机推荐

  • 广东海洋大学数学与计算机学院校友会,2020年广东海洋大学数学与计算机学院全日制硕士研究生入学考试复试及录取工作方案...

    为规范我校全日制硕士研究生复试工作 保障研究生入学质量 依据教育部有关文件及广东省研究生招生录取工作会议精神 结合学校今年硕士研究生招生工作的实际情况 特制定本工作方案 一 工作原则 研究生复试工作要坚持公开 公平 公正和科学选拔的原则 德
  • 【C++】继承详解

    文章目录 继承的概念 基类和派生类对象赋值转换 继承作用域 派生类的默认成员函数 继承和友元 静态成员变量的继承 菱形继承和虚拟继承 继承和组合 继承的概念 继承机制是面向对象程序设计使代码复用的重要手段 通过继承机制 可以利用已有的数据类
  • C++基础4:构造函数、析构函数、拷贝析构函数、静态成员函数

    构造函数 1 1构造函数 一个特殊的函数与类型名相同 没有返回值类型 保证创建一个对象时 自动调用一次 一个类可以有多个构造函数 作用 初始化对象 如果一个类不提供构造函数 则系统自动提供一个无参构造函数 但一旦提供构造函数 则系统的无参构
  • Head-Free Lightweight Semantic Segmentation with Linear Transformer 新颖的分割网络

    现有的语义分割网络基本都是编码解码结构 新的语义分割网络主要都是在解码阶段添加新的不同模块 提高解码阶段特征处理能力 从而实现语义分割 而这篇文章主要是去除了解码阶段 把工作重心放在了编码阶段 它采用并行架构来利用原型表示作为特定的可学习的
  • Linux Mii management/mdio子系统分析之六 fixed-mii_bus分析(mac2mac分析)

    前面几章我们介绍了MDIO模块的大部分内容 针对mii bus mdio bus phy device phy driver相关的注册 注销均进行了介绍 基本上把mdio模块的内容介绍完了 而本篇介绍的内容 主要是针对虚拟mii bus实现
  • python类基本语法笔记

    语言是工具 一段时间不用就会忘掉语法 静态方法和类方法 什么时候会用到这样的方法呢 类方法是针对类存在的 可以用类直接调用 主要用到的两个函数是staticmethod 和classmethod 简洁的用法是用Python的修饰器 需要注意
  • Vue总结第二天~自定义子组件、父子组件通信、插槽

    目录 一 组件 组件目录 1 注册组件 全局组件 局部组件和demo template模块 1 注册组件的基本步骤 2 全局组件demo 3 局部组件demo 4 template模块的简化 模板的分离写法 即将其内容封装到 templat
  • Matplotlib

    1 折线图 import matplotlib pyplot as plt import numpy as np x np linspace 1 1 50 1到1 有五十个点 y 2 x 1 plt figure num 1 figsize
  • 【计算机网络】第一章:计算机网络概述

    文章目录 1 1 计算机网络在信息时代的作用 1 2 因特网概述 1 3 三种交换方式 1 4 计算机网络的定义和分类 1 5 计算机网络的性能指标 1 6 计算机网络体系结构 计算机网络体系结构 计算机网络体系结构分层的必要性 计算机网络
  • 从gitHub当中更新项目synchronize Update fetch pull 项目的区别。

    11 从gitHub更新项目 方法一 右击你的项目 team synchronize workspace 这样他就会去gitHub那fetch回最新的版本 之后像svn一样 切换到team synchronize视图 注意服务器如有更新 而
  • Vue之插件的介绍

    简介 主要介绍Vue插件的概念 定义和使用 Vue的插件主要是用于增强功能 可以把它看作是一个工具库 可以提供很多强大的功能 比如一些强大的自定义指令 一些强大的工具方法 过滤器等 我们可以编写或者直接引入别人写的插件 就能获得强大的功能
  • odoo 权限

    创建安全组并分配用户 Odoo中的访问权限通过安全组成进行配置 给组指定权限 然后为组分配用户 每个功能区都有中枢应用所提供的基础安全组 在插件继承已有应用时 它们应对相应的组添加权限 参见本章稍后的向模型添加访问权限一节 在插件模块添中添
  • HDOJ 1058 Humble Numbers解题报告【DP】

    Humble Numbers 题目详见http acm hdu edu cn showproblem php pid 1058 开始拿到这个题目的时候还纠结了半天 英语很差的话这个题是不可能AC的 而我就是其中之一 Humber Numbe
  • spring-boot-maven-plugin报错的修改与版本号查看

    我报错的原因是因为没加版本号 版本号是多少 可以下个everything搜spring boot maven plugin 前面的号码就是版本号了
  • [转]出租车轨迹处理(二):时空分析

    接下来就要进行一些简单的分析了 今天的目标是如何对某一感兴趣区域进行出租车数据的时空分析 一 轨迹数据预处理 这一步在上一篇文章中已经有了介绍 步骤无非就是 1 使用pandas读取数据 import pandas as pd import
  • Matlab实现粒子群算法(附上完整仿真代码)

    粒子群算法 Particle Swarm Optimization PSO 是一种群体智能算法 通过模拟自然界中鸟群 鱼群等生物群体的行为 来解决优化问题 在PSO算法中 每个个体被称为粒子 每个粒子的位置表示解空间中的一个解 每个粒子的速
  • AVL树的插入与删除(均为递归实现)

    一 引言 AVL树是带有平衡条件的二叉查找树 这个平衡条件必须要容易保持 而且它必须保证树的深度是O logN 一颗AVL树是其每个节点的左子树和右子树的高度最多差一的二叉查找树 主要介绍插入算法和删除算法 二 AVL树的结点定义 type
  • (二)RK3566 Android11固件烧录

    上一篇 一 RK3566 Android11 系统编译 文章目录 1 固件包烧录步骤 2 固件统一打包 3 固件升级 1 固件包烧录步骤 烧录工具位置 RKTools windows AndroidTool AndroidTool Rele
  • e17 enlightenment 介绍及配置

    为什么要有一个窗口管理器 为什么一定要有一个桌面背景 甚至是标题栏 或是如果把一个应用程序如firefox当成桌面背景行不行 桌面能不能再快一点 我不想把资源浪费在那些用不到的地方 Linux那么多虚拟桌面 为什么我不能在一个桌面全屏运行一
  • python django框架ORM模型及ORM操作数据库 笔记

    ORM模型介绍 随着项目的越来越大 采用写原生SQL的方式在代码中会出现大量的SQL语句 那么问题就出现了 1 SQL语句重复利用率不高 越复杂的SQL语句条件越多 代码越长 会出现很多相近的SQL语句 2 很多SQL语句是在业务逻辑中拼出