一、数据库的准备工作
1、修改使用的数据库
一个实例对象就是一个记录,django默认使用sqlite3,用mysql需要更改配置文件。在settings修改DATABASES,默认用的是mysqldb。
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'books', #你的数据库名称
'USER': 'root', #你的数据库用户名
'PASSWORD': '', #你的数据库密码
'HOST': '', #你的数据库主机,留空默认为localhost
'PORT': '3306', #你的数据库端口
}
}
2、在项目的__init__文件中加上导入pymysql是语句
import pymysql
pymysql.install_as_MySQLdb()
3、打印日志,可以看见每一步操作的sql语句,在settings文件中加上下面的配置
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console':{
'level':'DEBUG',
'class':'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'propagate': True,
'level':'DEBUG',
},
}
}
4、在model.py建立表信息
创建一张表:
一个类就代表一张表,类的每个属性代表表中的每个字段。
class Book(models.Model): // 需要继承这个类,这样django才会知道这是一张表
name = models.CharField(max_length=20)
price = models.IntegerField()
author = models.CharField(max_length=20, null=True) # 可以为空,默认也是可以为空
模型常用的字段类型参数:
<1> CharField #字符串字段,要求必须有一个参数 max_length。
<2> IntegerField #用于保存一个整数.
<3> FloatField # 一个浮点数. 必须提供两个参数: 总位数,小数位数
<4> AutoField
# 一个 IntegerField, 添加记录时它会自动增长. 你通常不需要直接使用这个字段;
# 自定义一个主键:my_id=models.AutoField(primary_key=True)
# 如果你不指定主键的话,系统会自动添加一个主键字段到你的 model.
Field重要参数:
<1> null : 数据库中字段是否可以为空
<2> blank: django的 Admin 中添加数据时是否可允许空值
<3> default:设定缺省值
<4> primary_key:设置主键,如果没有设置django创建表时会自动加上:
id = meta.AutoField('ID', primary_key=True)
primary_key=True implies blank=False, null=False and unique=True. Only one
primary key is allowed on an object.
<5> unique:数据唯一
<6> verbose_name: Admin中显示的字段名称
<7> db_column: 设置数据库中字段的列名
<8> choices: Admin中显示选择框的内容,用不变动的数据放在内存中从而避免跨表操作,和form组件的choices一样,都是数据源。
<9> db_index: 数据库中该字段是否建立索引。作用是加速查找。
<10> unique: 数据库中该字段是否建立唯一索引。不仅可以加速查找,还可以去重。
<11> primary_key: 数据库中该字段是否为主键。不仅可以加速查找,还可以去重,并且非空。
<12> editable: Admin中是否可以编辑,会消失掉。
<13> help_text: Admin中该字段的提示信息。
<14> error_messages: 自定义错误信息(字典类型),从而定制想要显示的错误信息;
如:{'null': "不能为空.", 'invalid': '格式错误'}
<15> validators: 自定义错误验证(列表类型),从而定制想要的验证规则。
外键的参数:
to="Author" to代表绑定的表
to_field="id" 绑定的列名
related_name="b" 反向查找用.b , 以前反向查询是 小写的类名+_set 定义related_name以后 直接b就OK
related_query_name="b" 反向查找用.b_set,以前反向查询是 小写的类名+_set 定义related_query_name以后 b_set
on_delete # 当删除关联表中的数据时,当前表与其关联的行的行为
- models.CASCADE,删除关联数据,与之关联也删除
- models.DO_NOTHING,删除关联数据,引发错误IntegrityError
- models.PROTECT,删除关联数据,引发错误ProtectedError
- models.SET_NULL,删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空)
- models.SET_DEFAULT,删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值)
- models.SET(func) func为自定义的函数。
limit_choice_to # 在Admin或ModelForm中显示关联数据时,提供的条件
例如:limit_choices_to={'nid__gt': 5}
db_constraint=True # 是否在数据库中创建外键约束。如果没有外键约束,但是还是可以进行双下划线查询。
多对多参数:
to="Author" to代表绑定的表,多对多没有to_field 默认绑定的是主键。
related_name="b" 反向查找用.b , 以前反向查询是 小写的类名+_set 定义related_name以后 直接b就OK
related_query_name="b" 反向查找用.b_set,以前反向查询是 小写的类名+_set 定义related_query_name以后 b_set
limit_choices_to=None # 在Admin或ModelForm中显示关联数据时,提供的条件
例如:limit_choices_to=Q(nid__gt=10)
through="" # 自定义第三张表时,使用字段用于指定关系表,参数值是自己定义的第三张表名,可以用双下划线。
through_fields={} # 自定义第三张表时,使用字段用于指定关系表中哪些字段做多对多关系表
例如: through_fields={"u","t"}
symmetrical=True 不会创建反向查询的键 / false 会创建反向查询的键,和外键一样
db_constraint=True # 是否在数据库中创建外键约束
db_table="" # 默认创建第三张表时,数据库中表的名称
创建联合索引:
class Teacher(models.Model):
name = model.CharField(max_length=8)
age = model.IntegerField(max_length=8)
class Meta: # 定义一个类
db_table = "teacher" # 定义表名
unique_together = [("name","age"),] # 创建联合唯一索引
创建一个一对多的关系:
class Book(models.Model):
name = models.CharField(max_length=20)
price = models.IntegerField()
author = models.CharField(max_length=20, null=True) # 可以为空,默认也是可以为空
publish = models.ForeignKey("Publish", on_delete=models.CASCADE) // 表名(类名), 绑定字段, 删除时的操作(级联)
// 也可以这样写 publish = models.ForeignKey(Publish, on_delete=models.CASCADE), 这Publish类就必须写在前面。
class Publish(models.Model):
name = models.CharField(max_length=20)
city = models.CharField(max_length=10)
publish 代表一个外键对象,publish在数据库中的名字是publish_id, django自动加上了 "_id "。
※: django从1.9开始ForeignKey中的on_delete参数是必须的。topic = models.ForeignKey(Topic,on_delete=models.CASCADE) 。
创建完成后,执行下面两条指令。
python manage.py makemigrations
python manage.py migrate
二、单表对数据的增、删、改、查。
增加数据:
方法一:
Book.objects.create(name="PHP", price=99, author="Jax",publish_id=1)
方法二:
b = Book(name="PHP", price=99, author="Jax",publish_id=1)
b.save() // 不执行此方法,数据不会保存
当关联表插入时外键的插入方式:
方法一:
Book.objects.create(name="PHP", price=99, author="Jax",publish_id=1) // 直接插入数值
方法二:
pub = Publish.objects.filter(name="Jax")[0] // 此时取出的是queryset
Book.objects.create(name="PHP", price=99, author="Jax",publish=pub) // 直接插入对象
修改数据:
集合(queryset)有update方法,但是实例对象没有,实例直接修改属性值,记得save()。
方法一:
Book.objects.filter(name="Jax").update(price=100) // 先找到要修改的信息,然后再修改
方法二:
b = Book.objects.get(name="pp")
b.price=20 // 直接对其属性进行修改,相当于对字段直接进行修改
b.save()
推荐用方法一,方法二效率低。
删除数据:
Book.objects.filter(name="Jax").delete()
表的基本操作:
Book.objects.filter(条件) 返回的是一个条件集合,每个元素都是对象,集合不能调用多对多外键。
Book.objects.all() 查询所有结果
Book.objects.first() 查询第一条记录 返回一个对象,不可迭代。
Book.objects.last() 查询最后一条记录 返回一个对象,不可迭代。
Book.objects.get(条件) 取一条记录,当找不到数据或者找到多条数据时会报错。同时也无法用values方法,只能通过打点来调用属性。
Book.objects.values("字段名") 取出想取的字段,是字典格式,但是此时也是queryset对象,每个元素是字典格式
Book.objects.values_list("字段名") 是里面是元组格式
Book.objects.exclude() 与filter的结果相反,筛选不匹配的对象
Book.objects.order_by(字段) 排序
Book.objects.reverse() 反向排序,必须配合order_by使用,是对order by的反转
# order_by("-id","sid") reverse 以后 order_by("id","-sid") 负号代表降序(desc)
Book.objects.values("name").distinct() 去重,须和values()方法配合使用,否则没有意义。
Book.objects.values("name").distinct() .count() 计数
Book.objects.filter(name="name").extist() 判断是否有数据,在数据库中找到数据就回
Book.objects.filter(name="name").iterator() 对结果进行生成器的操作,占用内存小,但是每次迭代都需要执行一遍sql查询
Book.objects.all().select_related("c","d") 从数据库查询到外键的数据,这样就会从数据库全部拿到。
# select * from book left join author on book.c=auhtor.c and book.d=author.d
# Book.objects.all() select * from book
# 如果不添加select_related属性,每进行一次跨表查询,就到数据库查询一次。
Book.objects.all().prefetch_related("c") # 和select_related的功能一样,只不过是进行了两次数据查询
# 第一次先查主表,拿到FK对其进行去重,然后在子表进行第二次查询
# 第一次:SELECT blog_book.id, blog_book.name, blog_book.price, blog_book.author,blog_book.publish_id FROM blog_book
# 第二次:SELECT blog_publish.id, blog_publish.name, blog_publish.city FROM blog_publish WHERE blog_publish.id IN (1, 2, 3)
# select_related 进行了跨表查询,所以性能不够优
Book.objects.all().only("id","name") 只取id和name两列的数据。
# only和values的区别
# only拿到的Queryset的集合是对象的集合,对象封装了数据。
# values拿到的Queryset的集合是字典的集合。
Book.objects.all().defer("id","name") 除了id和name的两列,剩下的数据都取出来,和only方法相反。
Book.objects.all().using("default") 使用哪个数据库,根据setting的配置进行修改,默认是default。
obj,created = Book.objects.get_or_create(price=81, defaults={"name":".net","price":18})
# 先查询如果没有就创建price是查询条件,如果不存在按照defaults进行添加数据
# 返回两个值,一个是对象,一个是否创建对象。
obj,created = Book.objects.update_or_create(defaults={"name":"ruby","price":111},price=0)
# 先按照price找,找不到就按照defaults创建记录。
# 找到了就按照default的相应的字段进行更新。
objs ={
Book(id=10,name="Java",price="998",author="pp",publish_id=1),
Book(id=11,name="ruby",price="996",author="alex",publish_id=2)
}
Book.objects.bulk_create(objs,10) # 批量提交数据,10是batch_size 一次提交的数据量。
# 当发生异常会全部回滚
django写原生的SQL语句
方法一: 通过raw方法
# 查询的字段必须包含主键
book_list = Book.objects.all().raw("select * from blog_book") # book_list的结果为 是一个集合
for item in book_list:
print(item) # item是一个个对象
# 当查询其他表时,需要对其列名进行取别名
book_list = Book.objects.all().raw("select id as aid from blog_author")
方法二: 调用django原生的方法
from django.db import connection,connections
cursor = connection.cursor() # 创建游标
# cursor = connnections["default"].cursor() 也可以选择其他的数据库进行创建游标
cursor.execute("select * from book")
count = cursor.fetchone() # 查询一条记录
count1 = cursor.fetchmany(6) # 查询指定条数的记录
count2= cursor.fetchall() # 查询全部记录
方法三:extra 方法 extra(select=None, where=None, params=None, tables=None, order_by=None, select_params=None)
# Django的查询语法难以简单的表达复杂的 WHERE 子句,用extra方法,需要配合filter函数的使用
# django返回对象的个数是经过filter的个数,只有经过extra的方法返回值不为空
book_list = Book.objects.fiter(price="100").extra(where={"name=%s"}, param=("python",))
book_list = Book.objects.fiter(price="100").extra(select={"name":"select * from book where name=%s"}, select_param=("python"))
模糊查询:
book_list = Book.objects.filter(name__contains="p") name字段中包含“p”的结果
book_list = Book.objects.filter(name__icontains="p") name字段中包含“p”的结果,不区分大小写。
book_list = Book.objects.filter(price__gt=100) 筛选价格大于100
book_list = Book.objects.filter(price__lt=100) 筛选价格大于100
book_list = Book.objects.filter(price__range(90,110)) 查询在90--110的范围 等价于 between and
book_list = Book.objects.filter(price__in) 等价于in
book_list = Book.objects.filter(name__startwith="p") name字段以p开头
book_list = Book.objects.filter(name__endtwith="p") name字段以p结尾
__gte/__lte 大于等于/小于等于, istartwith/iendwith 以开头/结尾 不区分大小写
book_list = Book.objects.filter(name__isnull = True) 判断是否为空
一对多查询:
方法一:通过对象的操作
publish = Publish.objects.filter(id=1)[0]
print(publish.book_set.all().values("name", "price")) 通过publish表反向查找book表, 类名小写+_set 逆向查找该表。结合all()和filter()函数。
方法二:通过双下划线的操作
Book.objects.filter(publish__city="南方").values("name","price") // 直接查找publish表的city字段。
关联的表可以通过 定义的外键字段名+ "__"(双下划线)+ 字段名 可以直接查询到该表的字段,双下划线也可以用在values里面。
多对多查询:
多对多也可以进行自关联,用于互粉的功能。
需要创建一个多对多的外键
auth = models.ManyToManyField("Author") 参数是关联表的名字,会自动生成第三张表
查询:
obj = Book.objects.get(id=2)
print(obj.auth.all()) 此时取出的是一个queryset,并非一个对象。
abj = Author.objects.get(id=2)
print(abj.book_set.all()) 也可以通过反向查找
关于第三张表的查询,只能通过对象的属性赋值,通过定义的多对多的外键进行操作,不能直接操纵。
obj = Book.objects.get(id=1) 获取第三张表的book_id=1的书籍对象
abj = Author.objects.get(id=3) 获取第三张表的Author_id=3的作者对象
第一种添加记录的方法:
obj.auth.add(abj) 通过书籍的多对多的外键添加作者,对第三张表的book_id=1的对象添加一个Author等于3的对象。
第二种添加记录的方法:
obj.auth.add(1) 通过obj的多对多外键对象添加 book_id=1 且 Author_id=1 的记录
obj.auth.add(*[1,2,3]) 也可以添加一个列表,需要解包。
第一种删除记录的方法:
obj.auth.remove(abj) 删除记录, 删除第三张表的book_id=1,且Author_id=3的记录
第二种删除记录的方法
obj.auth.remove(2) 删除obj的多对多外键对象的id为2的记录
修改记录:
obj.auth.set([1,2,3]) 对obj进行修改,对于没有的记录进行添加,表中有的记录,但是set的列表中没有就删除表中的记录。
清空记录:
obj.auth.clear() 把book_id=1的数据全部清除
查询全部记录:
obj.auth.all() 会拿到所有的Author的对象。
多对多实际上就是一张包含两个外键的表。
关于一对多和多对多的反向查找:
反向查找用到 小写的类名_set 的方法,我们可以通过设置related_name的属性,可以不再使用 _set 的方法
models.py
class Book(models.Model):
name = models.CharField(max_length=20, verbose_name="姓名")
publish = models.ForeignKey("Publish", related_name="s", on_delete=models.CASCADE)
auth = models.ManyToManyField("Author",related_name="ssss")
class Author(models.Model):
name = models.CharField(max_length=20)
----------------------------------------------------------------
views.py
一对多:
Author.objects.get(id=1).s.all() #直接调用设置好的related_name,s代表关联表中外键的值
多对多:
Author.objects.get(id=1).ssss.all() #直接调用设置好的related_name,ssss代表关联表中外键的值
聚合函数和分组查询:
from django.db.models import Avg,Count,Sum
aggregate(*args,**kwargs)
Book.objects.all().aggregate(Avg("price")) 求平均值
也可以自己设置参数名:
Book.objects.all().aggregate(book_price=Avg("price"))
annotate(*args,**kwargs)
Book.objects.values("name").annotate(Sum("price")) 通过values先查出来name值,然后annotate()计算聚合函数
也可以自己设置参数名:
Book.objects.values("name").annotate(sp=Sum("price"))
F查询和Q查询:
需要导入 from django.db.models import F,Q
F查询:使用查询条件的值,专门取对象中某列值的操作
Book.objects.all().update(price=F("price")+10) 在原来价格的基础上加10
Q查询:构建"或关系"搜索条件,也可以使用双下划线。
" | " 代表或,"~"代表非,"&"代表且
ret = Book.objects.filter(Q(price=76) | Q(name="python")).values("name")
ret = Book.objects.filter(Q(price=76) | ~Q(name="python")).values("name")
Q查询也可以和关键字查询也可以一起使用,Q查询必须放在最前面
※:使用双下划线只能在filter()、values()方法中使用。其他的地方通过打点的方法来调用属性。
Queryset的特性:
可以切片,也可以进行迭代。
惰性求值:
Book.objects.get(id=6) 当只执行不打印结果时,这句话没有执行sql,只是生成了对象,当什么时候用才会执行。
for循环的情况:
ret = Book.objects.filter(id="3")
for item1 in ret: # 执行sql
print(item1)
for item2 in ret: # 不会执行sql,直接从缓存中取数据
print(item2)
在前端页面上可以通过 get_+字段名+_display,直接显示这个字段的choice对应的中文数据。
{
{ item.get_status_display }}