当我们新建一个应用时,这个应用通常会自带一个test.py的文件这个文件就是测试文件。我们学要把测试所需的环境搭建起来,然后在文件书写要测试的代码右键直接run test.py就可以了,不需要再把django项目启动起来
此时控制台会输出代码运行的结果(熟悉的感觉又回来了)
测试环境的搭建如下代码
from django.test import TestCase
# Create your tests here.
import os
import sys
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "Mysite03.settings")
import django
django.setup()
print('就一个字,帅!') # 就一个字,帅!
例如:想测试往数据库里添加数据是否成功
from django.test import TestCase
# Create your tests here.
import os
import sys
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "Mysite03.settings")
import django
django.setup()
from app01 import models
reg_obj = models.student.objects.create(
student_name='小张',
student_account='xiaozhang',
student_pwd='123456',
student_age='22',
student_hobby='1'
)
see_obj = models.student.objects.filter(student_account='xiaozhang').first()
print(see_obj)
# 就一个字,帅!
# 小张
queryset是查询集,就是传到服务器上的url里面的内容。Django会对查询返回的结果集QuerySet进行缓存,这里是为了提高查询效率。也就是说,在你创建一个QuerySet对象的时候,Django并不会立即向数据库发出查询命令,只有在你需要用到这个QuerySet的时候才回去数据库查询。
print(models.student.objects.all())
返回的是所有queryset对象
# , , , ]>
print(models.student.objects.filter(student_name='小张'))
# ]>
get_something = models.student.objects.get(id=3)
print(get_something,get_something.student_age)
# egon 19
get_something = models.student.objects.get(id=100)
print(get_something,get_something.student_age)
# app01.models.DoesNotExist: student matching query does not exist.
print(models.student.objects.exclude(student_is_delete=True))
# , , ]>
print(models.student.objects.values('student_is_delete'))
#
print(models.student.objects.values_list('student_is_delete'))
#
print(models.student.objects.order_by('student_age')) # 默认按升序排列
# , , , ]>
print(models.student.objects.order_by('student_age').reverse())
# , , , ]>
print(models.student.objects.all().count())
# 4
print(models.student.objects.order_by('student_age').reverse().first())
# ly
print(models.student.objects.order_by('student_age').reverse().last())
# jason
print(models.student.objects.filter(student_name='小张').exists())
# True
models.Tb1.objects.filter(id__lt=10, id__gt=1) # 获取id大于1 且 小于10的值
models.Tb1.objects.filter(id__in=[11, 22, 33]) # 获取id等于11、22、33的数据
models.Tb1.objects.exclude(id__in=[11, 22, 33]) # not in
models.Tb1.objects.filter(name__contains="ven") # 获取name字段包含"ven"的
models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感
models.Tb1.objects.filter(id__range=[1, 3]) # id范围是1到3的,等价于SQL的bettwen and
类似的还有:startswith,istartswith, endswith, iendswith
date字段还可以:
models.Class.objects.filter(first_day__year=2017)
date字段可以通过在其后加__year,__month,__day等来获取date的特点部分数据
# date
#
# Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1))
# Entry.objects.filter(pub_date__date__gt=datetime.date(2005, 1, 1))
# year
#
# Entry.objects.filter(pub_date__year=2005)
# Entry.objects.filter(pub_date__year__gte=2005)
# month
#
# Entry.objects.filter(pub_date__month=12)
# Entry.objects.filter(pub_date__month__gte=6)
# day
#
# Entry.objects.filter(pub_date__day=3)
# Entry.objects.filter(pub_date__day__gte=3)
# week_day
#
# Entry.objects.filter(pub_date__week_day=2)
# Entry.objects.filter(pub_date__week_day__gte=2)
需要注意的是在表示一年的时间的时候,我们通常用52周来表示,因为天数是不确定的,老外就是按周来计算薪资的哦~
models.py
"""
需要明确的关系:
出版者对书的关系为一对多(一个出版社可以出版多本书)
书和作者的关系为多对多的关系(多个作者可以出版一本书,一本书也可以有多个作者)
作者和作者详情是一对一关系(一个作者只能由一个作者详情)
"""
# 创建书这张表
class Book(models.Model):
title = models.CharField(max_length=32,verbose_name='书名')
price = models.DecimalField(max_digits=8,decimal_places=2,verbose_name='书价格')
# 出版社对书
publish = models.ForeignKey(to='Publish')
# 书对作者
author = models.ManyToManyField(to='Authors')
# 创建出版社表
class Publish(models.Model):
name = models.CharField(max_length=32,verbose_name='出版社名')
addr = models.CharField(max_length=64,verbose_name='出版社地址')
Email= models.EmailField() # 电子邮件格式
# 创建作者表
class Authors(models.Model):
name = models.CharField(max_length=32,verbose_name='作者名')
phone = models.BigIntegerField(verbose_name='作者联系方式')
# 作者对作者详情
detail = models.OneToOneField(to='Authors_detail')
# 创建作者详情表
class Authors_detail(models.Model):
addr = models.CharField(max_length=64,verbose_name='作者地址')
hobby = models.CharField(max_length=32,verbose_name='作者爱好')
test.py
from django.test import TestCase
# Create your tests here.
import os
import sys
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "Mysite03.settings")
import django
django.setup()
from app01 import models
# 外键的增加
# 一对多
# 第一种publish_id
models.Book.objects.create(title='三国演义',price=123.56,publish_id=1)
models.Book.objects.create(title='金瓶梅',price=786.01,publish_id=2)
models.Book.objects.create(title='西游记',price=333.56,publish_id=3)
models.Book.objects.create(title='红楼梦',price=456.56,publish_id=2)
# 第二种虚拟字段 直接放对象用publish接收
obj = models.Publish.objects.filter(pk=1).first()
models.Book.objects.create(title='水浒传',price=111.26,publish=obj)
obj = models.Publish.objects.filter(pk=2).first()
models.Book.objects.create(title='西游记', price=111.26, publish=obj)
models.Publish.objects.create(name='清华出版社',addr='北京',Email='[email protected]')
models.Publish.objects.create(name='北大出版社',addr='北京',Email='[email protected]')
# 外键的删除
models.Book.objects.filter(publish_id=3).delete() # 级联删除
# 外键的修改
# 第一种方式直接使用publish_id
models.Book.objects.filter(pk=2).update(publish_id=4)
# 第二种方式使用虚拟字段 publish接收
obj = models.Publish.objects.filter(pk=2).first()
models.Book.objects.filter(pk=2).update(publish=obj)
# 多对多 就是在操作第三张关系表,就是对第三张虚拟表的增删改查(book_author)
# 如何给书籍添加作者
# 第一种方式
book = models.Book.objects.filter(pk=2).first()
book.author # 先拿到book对象,对象可以点属性,(book.author)这就是第三张表
book.author.add(1) # 给书籍id为2 添加作者id为1的作者
# 书和作者是多对多的关系 add()也可以添加多条
book.author.add(2,3)
# 第二种方式
author_obj = models.Authors.objects.filter(pk=1).first()
author_obj1 = models.Authors.objects.filter(pk=2).first()
book = models.Book.objects.filter(pk=2).first()
book.author.add(author_obj,author_obj1)
"""
add给第三张表添加数据,括号内可以传数字也可以传对象并且可以传多个
"""
# 神奇的双下划线查询
# 1 年龄大于35岁的数据
res = models.User.objects.filter(age__gt=35)
print(res)
# 2 年龄小于35岁的数据
res = models.User.objects.filter(age__lt=35)
print(res)
# 大于等于 小于等于
res = models.User.objects.filter(age__gte=32)
print(res)
res = models.User.objects.filter(age__lte=32)
print(res)
# 年龄是18 或者 32 或者40
res = models.User.objects.filter(age__in=[18,32,40])
print(res)
# 年龄在18到40岁之间的 首尾都要
res = models.User.objects.filter(age__range=[18,40])
print(res)
# 查询出名字里面含有s的数据 模糊查询
res = models.User.objects.filter(name__contains='s')
print(res)
# 是否区分大小写 查询出名字里面含有p的数据 区分大小写
res = models.User.objects.filter(name__contains='p')
print(res)
# 忽略大小写
res = models.User.objects.filter(name__icontains='p')
print(res)
res =models.User.objects.filter(name__startswith='j')
res1 = models.User.objects.filter(name__endswith='j')
print(res,res1)
# 查询出注册时间是 2020 1月
res = models.User.objects.filter(register_time__month='1')
res = models.User.objects.filter(register_time__year='2020')
# 正向
# 反向
根据外键来判断
eg:外键在A那,A查B就是正向查找。B查A就是反向查找
正向查询按字段
反向查询按表名小写
# 1.查询书籍主键为1的出版社
# 正向查找
print(models.Book.objects.filter(pk=1).values('id','publish__name')) #
# 2.查询书籍主键为2的作者
# 正向查找
#
print(models.Book.objects.filter(pk=2).values('id','author__name'))
# 3.查询作者lanlan的爱好
# 正向查找
#
print(models.Authors.objects.filter(name='lanlan').values('name','detail__hobby'))
# 4.查询出版社是清华出版社出版的书
# 反向查找
#
print(models.Publish.objects.filter(name='清华出版社').values('name','book__title'))
# 5.查询作者是lanlan写过的书
# 反向查找
#
print(models.Authors.objects.filter(name='lanlan').values('name','book__title'))
# 6.查询爱好的打球的作者姓名
# 反向查找
# < QuerySet[{'authors__name': 'zhangzhang', 'hobby': '打球'}] >
print(models.Authors_detail.objects.filter(hobby='打球').values('authors__name','hobby'))
"""
基于对象
反向查询的时候
当你的查询结果可以有多个的时候 就必须加_set.all()
当你的结果只有一个的时候 不需要加_set.all()
自己总结出 自己方便记忆的即可 每个人都可以不一样
"""
# 1.查询zhenzhen的住址,爱好和作者姓名
# 正向查找
#
print(models.Authors.objects.filter(name='zhenzhen').values('name','detail__addr','detail__hobby'))
# 反向查找
#
print(models.Authors_detail.objects.filter(authors__name='zhenzhen').values('authors__name','addr','hobby'))
# 2.查询书籍主键为1的出版社名称和书的名称
# 正向查找
#
print(models.Book.objects.filter(pk=1).values('title','publish__name'))
# 反向查找
#
print(models.Publish.objects.filter(book__pk=1).values('book__title','name'))
# 3.查询书籍主键为1的作者姓名
# 正向查找
#
print(models.Book.objects.filter(pk=1).values('author__name'))
#
print(models.Authors.objects.filter(book__id=1).values('name'))
# 4.查询书籍主键是1的作者爱好
# 正向查找
#
print(models.Book.objects.filter(pk=1).values('author__detail__hobby'))
# 反向查找
# < QuerySet[{'hobby': '跳水'}] >
print(models.Authors_detail.objects.filter(authors__book__id=1).values('hobby'))
"""
聚合查询:使用聚合函数进行查询
分组查询需要使用 aggregate关键字
"""
# 使用聚合函数必须导入聚合函数的模块
from django.db.models import Avg,Max,Min,Sum,Count
print(models.Book.objects.aggregate(
price_avg=Avg('price'),
price_sum=Sum('price'),
price_count=Count('price'),
price_max=Max('price'),
price_min=Min('price'))
)
# { 'price_avg': 320.33,
# 'price_sum': Decimal('1601.65'),
# 'price_count': 5,
# 'price_max': Decimal('786.01'),
# 'price_min': Decimal('111.26')
# }
"""
分组查询:group by
分组查询需要使用 annotate关键字
"""
# 统计每一本书的作者个数
# print(models.Book.objects.annotate()) # models后面点什么就按什么分组
#
print(models.Book.objects.annotate(author_num=Count('author')).values('author_num','title'))
# 统计每个出版社卖出的最便宜的价格
#
print(models.Publish.objects.annotate(min_price=Min('book__price')).values('name','min_price'))
# 统计不止一个作者的图书
#
print(models.Book.objects.annotate(count_author=Count('author')).filter(count_author__gt=1).values('title','count_author'))
"""
只要你的ORM语句得到的结果还是一个Queryset对象
那么你就可以一直点Queryset对象封装方法(无限制,只要你的逻通)
"""
# 查询每个作者出的书的总价格
#
print(models.Authors.objects.annotate(sum_price = Sum('book__price')).values('name','sum_price'))
"""
按照指定指定字段分组如何做?
models.Book.objects.values('price').annotate()
这种就是按照在指定字段分组
"""
# 为了更加的直观模拟出F与Q的操作,先在book表中增加两条字段
class Book(models.Model):
title = models.CharField(max_length=32,verbose_name='书名')
price = models.DecimalField(max_digits=8,decimal_places=2,verbose_name='书价格')
stock = models.IntegerField(default=1000,verbose_name='库存数量')
sale = models.IntegerField(default=100,verbose_name='卖出数量')
# 出版社对书
publish = models.ForeignKey(to='Publish')
# 书对作者
author = models.ManyToManyField(to='Authors')
def __str__(self):
return '书名:%s'%self.title
# test.py
from django.db.models import F,Q
# 查出卖出数大于库存数的书籍
# F: An object capable of resolving references to existing query objects.(能够解析对现有查询对象的引用的对象。)
print(models.Book.objects.filter(sale__gt=F('stock')))
# , , ]>
# 将所有书籍的价格提升五十块
print(models.Book.objects.update(price=F('price') + 500))
# 将所有书的名字加上“爆款”两个字
"""
在操作字符的时候,F不能够实现字符串的拼接
如果非要实现,需要导Concat模块和Value模块
from django.db.models.functions import Concat
from django.db.models import Value
"""
from django.db.models.functions import Concat
from django.db.models import Value
print(models.Book.objects.update(title=Concat(F('title'),Value('爆款'))))
print(models.Book.objects.update(title=F('title')+'爆款')) # 所有title名称会变空白
# Q:Encapsulates filters as objects that can then be combined logically (using
# `&` and `|`) (将过滤器封装为对象,然后可以逻辑地组合这些对象(使用' & ' and ' | '))
# 查询卖出数大于100小于300
print(models.Book.objects.filter(Q(sale__gt=100, sale__lt=300)).values('title'))
#
print(models.Book.objects.filter(Q(sale__gt=100), Q(sale__lt=300)).values('title'))
#
#print(models.Book.objects.filter(Q(sale__gt=100)|Q(sale__lt=300)).values('title')) # | or 或者关系
print(models.Book.objects.filter(~Q(sale__gt=100) | Q(sale__lt=300)).values('title')) # ~是取反的
#
# Q的高级用法 能够将查询条件左边的也变成字符串样式
q =Q()
q.children.append(('sale__gt',100))
q.children.append(('sale__lt',300))
print(models.Book.objects.filter(q)) # 默认是and关系 , ]>
# 可以修改成or 关系
q.connector = 'or'
print(models.Book.objects.filter(q)) # or关系 , , , , ]>
"""
事务
ACID
原子性:不可分割最小单位
一致性:跟原子性是相辅相成的
隔离性:事物之间互相不干扰
持久性:事务一旦确认永久生效
事务的回滚
rollback
事物的确认
commit
"""
# 事务
# 需要导入transaction模块
from django.db import transaction
# 开启事务
try: # 异常捕获(视情况而定)
with transaction.atomic():
pass
# 代码块
# 在with代码内书写的所有ORM操作都属于同一个事务
except Exception as e:
print(e)
AutoField(Field)
- int自增列,必须填入参数 primary_key=True
BigAutoField(AutoField)
- bigint自增列,必须填入参数 primary_key=True
注:当model中如果没有自增列,则自动会创建一个列名为id的列
from django.db import models
class UserInfo(models.Model):
# 自动创建一个列名为id的且为自增的整数列
username = models.CharField(max_length=32)
class Group(models.Model):
# 自定义自增列
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
SmallIntegerField(IntegerField):
- 小整数 -32768 ~ 32767
PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
- 正小整数 0 ~ 32767
IntegerField(Field)
- 整数列(有符号的) -2147483648 ~ 2147483647
PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
- 正整数 0 ~ 2147483647
BigIntegerField(IntegerField):
- 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807
BooleanField(Field)
- 布尔值类型
NullBooleanField(Field):
- 可以为空的布尔值
CharField(Field)
- 字符类型
- 必须提供max_length参数, max_length表示字符长度
TextField(Field)
- 文本类型
EmailField(CharField):
- 字符串类型,Django Admin以及ModelForm中提供验证机制
IPAddressField(Field)
- 字符串类型,Django Admin以及ModelForm中提供验证 IPV4 机制
GenericIPAddressField(Field)
- 字符串类型,Django Admin以及ModelForm中提供验证 Ipv4和Ipv6
- 参数:
protocol,用于指定Ipv4或Ipv6, 'both',"ipv4","ipv6"
unpack_ipv4, 如果指定为True,则输入::ffff:192.0.2.1时候,可解析为192.0.2.1,开启此功能,需要protocol="both"
URLField(CharField)
- 字符串类型,Django Admin以及ModelForm中提供验证 URL
SlugField(CharField)
- 字符串类型,Django Admin以及ModelForm中提供验证支持 字母、数字、下划线、连接符(减号)
CommaSeparatedIntegerField(CharField)
- 字符串类型,格式必须为逗号分割的数字
UUIDField(Field)
- 字符串类型,Django Admin以及ModelForm中提供对UUID格式的验证
FilePathField(Field)
- 字符串,Django Admin以及ModelForm中提供读取文件夹下文件的功能
- 参数:
path, 文件夹路径
match=None, 正则匹配
recursive=False, 递归下面的文件夹
allow_files=True, 允许文件
allow_folders=False, 允许文件夹
FileField(Field)
- 字符串,路径保存在数据库,文件上传到指定目录
- 参数:
upload_to = "" 上传文件的保存路径
storage = None 存储组件,默认django.core.files.storage.FileSystemStorage
ImageField(FileField)
- 字符串,路径保存在数据库,文件上传到指定目录
- 参数:
upload_to = "" 上传文件的保存路径
storage = None 存储组件,默认django.core.files.storage.FileSystemStorage
width_field=None, 上传图片的高度保存的数据库字段名(字符串)
height_field=None 上传图片的宽度保存的数据库字段名(字符串)
DateTimeField(DateField)
- 日期+时间格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]
DateField(DateTimeCheckMixin, Field)
- 日期格式 YYYY-MM-DD
TimeField(DateTimeCheckMixin, Field)
- 时间格式 HH:MM[:ss[.uuuuuu]]
DurationField(Field)
- 长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型
FloatField(Field)
- 浮点型
DecimalField(Field)
- 10进制小数
- 参数:
max_digits,小数总长度
decimal_places,小数位长度
BinaryField(Field)
- 二进制类型
ORM字段与MySQL字段对应列表
对应关系:
'AutoField': 'integer AUTO_INCREMENT',
'BigAutoField': 'bigint AUTO_INCREMENT',
'BinaryField': 'longblob',
'BooleanField': 'bool',
'CharField': 'varchar(%(max_length)s)',
'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
'DateField': 'date',
'DateTimeField': 'datetime',
'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
'DurationField': 'bigint',
'FileField': 'varchar(%(max_length)s)',
'FilePathField': 'varchar(%(max_length)s)',
'FloatField': 'double precision',
'IntegerField': 'integer',
'BigIntegerField': 'bigint',
'IPAddressField': 'char(15)',
'GenericIPAddressField': 'char(39)',
'NullBooleanField': 'bool',
'OneToOneField': 'integer',
'PositiveIntegerField': 'integer UNSIGNED',
'PositiveSmallIntegerField': 'smallint UNSIGNED',
'SlugField': 'varchar(%(max_length)s)',
'SmallIntegerField': 'smallint',
'TextField': 'longtext',
'TimeField': 'time',
'UUIDField': 'char(32)',
# 创建一张普通表
class user(models.Model):
choices_gender =(
(1,'男'),
(2,'女'),
(3,'保密')
)
name = models.CharField(max_length=32,verbose_name='姓名')
gender = models.IntegerField(choices=choices_gender,verbose_name='性别')
# 向表中添加数据
# models.user.objects.create(name='lanlan',gender=2)
# models.user.objects.create(name='honghong',gender=1)
# models.user.objects.create(name='lvlv',gender=3)
# 从表中取数据
res = models.user.objects.filter(name='lanlan').first()
print(res.gender) # 2 只能取出数字,取不出具体的内容
"""
在获取值的时候,固定句式:get_字段名_display() =>
"""
print(res.get_gender_display()) # 女
res1 = models.user.objects.filter(name='lvlv').first()
print(res1.get_gender_display()) # 保密
# 第一种
"""
全自动创建
优点:可以利用正反向查询的概念,还可以利用add(),set(),remove(),clear()
缺点:扩展性差(第三张表只能有三个字段,写死了。不能添加其他字段)
"""
class Book(models.Model):
title = models.CharField(max_length=32,verbose_name='书名')
authors = models.ManyToManyField(to='Authors')
class Authors(models.Model):
name = models.CharField(max_length=32,verbose_name='作者名')
# 第二种
"""
纯手动创建
特点:add(),set(),remove(),clear()不能用
不可以使用正反向查询概念
扩展性强
"""
class Book(models.Model):
title = models.CharField(max_length=32,verbose_name='书名')
class Authors(models.Model):
name = model.CharField(max_length=32,verbose_name='作者名')
class BooktoAuthor(model.Model):
book_id= models.ForeignKey(to='Book')
author_id= models.ForeignKey(to='Author')
# 手动创建的第三张表,可以自己定义其他字段
# 半自动
"""
半自动创建
特点:可以利用正反向的概念,但是不能用add(),set(),remove(),clear()
扩展性强
"""
class Book(models.Model):
title = models.CharField(max_length=32,verbose_name='书名')
authors = models.ManyToManyField(to='Author',
through='BooktoAuthor', # 指定哪个表为第三张表
through_fields=('book','authors') # 外键放在谁那,谁在第一个位置
)
class Author(models.Model):
name = model.CharField(max_length=32,verbose_name='作者名')
'''
book = models.ManyToManyField(to='Book',
through='BooktoAuthor', # 指定哪个表为第三张表
through_fields=('authors','book') # 外键放在谁那,谁在第一个位置
)
'''
class BooktoAuthor(model.Model):
book= models.ForeignKey(to='Book')
author= models.ForeignKey(to='Author')
# 手动创建的第三张表,可以自己定义其他字段