额外包
mysqlclient :mysql需求 pymysql :mysql需求 pillow :图片文件需求
class FileLibrary(models.Model):
# 文件名
name = models.CharField(max_length=100)
修改模型后的操作
python manage.py makemigrations
【app名称】,migrations中创建或修改模型对应文件python manage.py migrate
,将这些改变更新到数据库中。
字段类型
AutoField | 自增字段,通常不用自己设置 |
Booleanfield | 布尔字段,值为True或False |
NullBooleanField | 布尔字段,包含空值:Null、True、False |
CharField | 字符串,必须有max_length表示最大字符个数 |
TextField | 大文本字段,一般超过4000个字符时使用 |
JSONField | 以JSON格式存储 |
URLField | 存储URL的字符串,默认长度200;verify_exists(True),检查URL可用性 |
SlugField | 只能包含字母,数字,下划线和连字符的字符串,通常被用于URLs表示 |
CommaSeparatedIntegerField | 以逗号间隔的整数序列,必须有max_length |
IntegerField | 整数[-2147483648,2147483647 ] |
PositiveIntegerField | 正整数[0 ,2147483647] |
PositiveSmallIntegerField | 正短整数[0 ,32767] |
SmallIntegerField | 小整数[-32768 ,32767] |
BigIntegerField | 64位的整型数值 (-2^63) – (2^63-1) |
BinaryField | 存储原始的2进制数据,功能有限,仅支持字节分配 |
Decimalfield | 十进制浮点数 , 参数maxdigits表示总位数, 参数decimalplaces表示小数位数 |
FloatField | 浮点数 |
DateField | 日期, 参数auto_now表示每次保存对象时,自动设置该字段为当前时间,默认为False; 参数auto_now_add表示当对象第一次被创建时自动设置当前时间,默认为False; 参数auto_now_add和auto_now是相互排斥的,组合将会发生错误 |
TimeField | 时间,参数同date |
DateTimeField | 日期时间,参数同date |
EmailField | 带有email合法性检测的一个CharField |
IPAddressField | 点分十进制表示的IP地址,如10.0.0.1 |
GenericIPAddressField | ip v4和ip v6地址表示,ipv6遵循RFC 4291section 2.2 |
FileField | 有一个必须参数upload_to,用于保存文件的本地文件系统 |
FilePathField | 一个CharField,用来选择文件系统下某个目录里面的某些文件,它有三个专有参数,只有path是必须的。path是一个目录的绝对路径,match是一个正则表达式字符串,用来过滤文件名称;recursive为bool,指定是否包含path下的子目录。 |
imageField | 继承于FileField,对上传的 内容进⾏行行校验,确保是有效的图⽚片(必须要安装Pillow才可以使用(pip install Pillow) |
ForeignKey | 外键,参数一链接的模型,参数二on_delte,级联删除,一般为 on_delete=models.CASCADE |
ManyToManyField | 多对多 add()添加 remove()移除 |
参数
null | 如果为True,表示允许为空,默认值是False |
blank | 如果为True,则该字段允许为空白,默认值是False |
db_column | 字段的名称,如果未指定,则使用属性的名称 |
db_index | 若值为True, 则在表中会为此字段创建索引,默认值是False |
default | 默认 |
primary_key | 若为True,则该字段会成为模型的主键字段,默认值是False,一般作为AutoField |
unique | 如果为True, 这个字段在表中必须有唯一值,默认值是False |
模型方法
class HighRatingManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(rating=1)
# CHOICES选项
class Rating(models.IntegerChoices):
VERYGOOD = 1, 'Very Good'
GOOD = 2, 'Good'
BAD = 3, 'Bad'
class Product(models.Model):
# 数据表字段
name = models.CharField('name', max_length=30)
rating = models.IntegerField(max_length=1, choices=Rating.choices)
# MANAGERS方法
objects = models.Manager()
high_rating_products =HighRatingManager()
# META类选项
class Meta:
abstract=True # 指定该模型为抽象模型,不会在数据库里创建数据表
proxy=True # 指定该模型为代理模型,即克隆一个与模型A相同的模型B
verbose_name = 'product' # 为模型设置便于人类阅读的别名
verbose_name_plural = 'products' # 为模型设置便于人类阅读的别名
db_table= xxx # 自定义数据表名,属性为字符串
odering=['-pub-date'] # 自定义按哪个字段排序,-代表逆序
permissions=[] # 为模型自定义权限
managed=False # 默认为True,如果为False,Django不会为这个模型生成数据表,即是否支持数据迁移命令
indexes=[] # 为数据表设置索引,对于频繁查询的字段,建议设置索引
constraints= # 给数据库中的数据表增加约束。
# __str__方法
def __str__(self):
return self.name
# 重写save方法
def save(self, *args, **kwargs):
do_something()
super().save(*args, **kwargs)
do_something_else()
# 定义单个对象绝对路径
def get_absolute_url(self):
return reverse('product_details', kwargs={'pk': self.id})
# 其它自定义方法
def do_something(self):
示例
class UserInfo(models.Model):
# 邮箱
uname = models.EmailField()
# 密码经加密后长度会变长
pwd = models.CharField(max_length=60)
class Meta:
ordering = ('id',)
class CartItem(models.Model):
goodsid = models.PositiveIntegerField()
colorid = models.PositiveIntegerField()
sizeid = models.PositiveIntegerField()
count = models.PositiveIntegerField()
isdelete = models.BooleanField( default=False)
user = models.ForeignKey(UserInfo,on_delete=models.CASCADE) #外键
增加
_obj = {
'goodsid':goodsid,
'colorid':colorid,
'sizeid':sizeid,
'count':count,
'user':user
}
CartItem.objects.create(**_boj)
批量增加
a = [1,2,3]
arr = []
for i in a:
arr.append(User(cid=i))
User.objects.bulk_create(arr)
删除
user=User.object.get(id=1)
user.delete()
users=User.objects.all()
users.delete()
更新
update只适用于filter查询结果
user.cartitem_set.filter(
goodsid=goodsid,
colorid=colorid,
sizeid=sizeid
).update(count=count,isdelete=False)
单个结果用save
config.json = "hoge"
config.save()
更新或新增
book.objects.update_or_create(defaults={'name':'安尔达'},id=5)
# defaults中的内容为查询内容,后面的是需要更新的内容。
批量更新
querys = User.objects.all()
arr = []
for i in querys:
if i.state == 3:
o = User(id=i.id,type=3,sum_fans=0)
if i.start == 4:
o = User(id=i.id,type=4,sum_fans=2)
else:
o = User(id=i.id,type=5,sum_fans=5)
arr.append(o)
User.objects.bulk_update(arr, ['type', 'sum_fans'])
查询
userList = UserInfo.objects.filter(uname=uname,pwd=pwd)
user = userList[0]
cart = user.cartitem_set.filter(goodsid=goodsid,colorid=colorid,sizeid=sizeid)
cart.count() #查到的数量
# order_by('id') 按id升序 / order_by('-id') 按id降序
user.cartitem_set.order_by('id').filter(isdelete=False).all()
#根据goodsid,sizeid,colorid获取CartItem对象
user.cartitem_set.get(goodsid=goodsid,sizeid=sizeid,colorid=colorid)
get的参数只能是model中定义的哪些字段,只支持严格匹配
filter的参数可以是字段也可以是扩展的where查询关键字,如in,like
get返回值是一个定义的model对象
filter返回值是一个新的QuerySet对象,然后可以对QuerySet在进行查询返回新的QuerySet对象,支持链式操作,QuerySet一个集合对象,可使用迭代或者遍历,切片等,但是不等于list类型(是一个object对象集合)
get只有一条记录返回的时候才正常,也就是说明get查询字段必须是主键或者唯一约束的字段。当返回多条记录或者没有找到记录的时候都会抛出异常
get方法是从数据库的取得一个匹配的结果,返回一个对象,如果记录不存在的话,它会报错,有多条记录也会报错。
filter有没有匹配的记录都可以
filter方法是从数据库的取得匹配的结果,返回一个对象列表,如果记录不存在的话,它会返回[]。
另外,从别的资料里看到filter好像有缓存数据的功能,第一次查询数据库并生成缓存,下次再调用filter方法的话,直接取得缓存的数据,会get方法每次执行都是直接查询数据库的,不知道这个是不是正确,看看就好。
模糊查询
1.查询某个字符串是否在指定的字段中
article1 = models.Article.objects.filter(id__contains=1) # 区分大小写
article2 = models.Article.objects.filter(id__icontains = 1) #不区分大小写
2.指定某个字段的是否在某个集合中
articles = models.Article.objects.filter(id__in=[1,2,3])
for article in articles:
print(article)
3.大于、大于等于、小于、小于等于
gt、gte、lt、lte
articles = models.Article.objects.filter(id__gt=2)
# 在 ... 之间,左闭右闭区间,= 号后面为两个元素的列表
articles = models.Article.objects.filter(id__range=[10,20])
4.某个值开始、以某个值结束
字段以指定字符开头与结尾
articles = Article.objects.filter(title__startwith="hello")
articles = Article.objects.filter(title__endswith="hello")
articles = Article.objects.filter(title__istartswith="hello") # 不区分大小写
articles = Article.objects.filter(title__iendswith="hello")
import datetime
from django.utils.timezone import make_aware
start_time = make_aware(datetime(year=2018,month=11,day=2,hour=16,minute=0,second=0))
end_time = make_aware(datetime(year=2018,month=11,day=2,hour=17,minute=0,second=0))
articles = models.Article.objects.filter(create_time__range=(start_time,end_time))
print(articles)
print(articles.query)
5.date
articles = models.Article.objects.filter(create_time__date = datetime(year=2018,month=11,day=2))
print(articles.query)
print(articles)
然后添加映射,然后我们发现并没有查找出结果出来,但是我们是刚才添加的信息,时间也是设置为刚才的,为什么没有信息呢。
原因是因为MySQL默认是没有储存时区相关的信息,但是在执行sql语句时我们发现对时区进行了装换,所以时不会得到我们想要的结果的。因此我们需要下载一些时区表的文件,然后添加到mysql的配置路径中。
windows中
在http://dev.mysql.com/downloads/timezones.html下载timezone_2018d_posix.zip - POSIX standard。然后将下载下来的所有文件拷贝到C:\ProgramData\MySQL\MySQL Server 5.7\Data\mysql中,如果提示文件名重复,那么选择覆盖即可。
linux或者mac系统
在命令行中执行以下命令:mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -D mysql -u root -p,然后输入密码,从系统中加载时区文件更新到mysql中。
然后对上面的代码不需要进行改动,也能执行成功了
6.year/month/day:根据年/月/日进行查找
articles = models.Article.objects.filter(create_time__year=2018)
articles = models.Article.objects.filter(pub_date__year__gte=2017)
7.week_day: 根据星期来进行查找
1表示星期天,7表示星期六,2-6代表的是星期一到星期五。比如要查找星期三的所有文章,那么可以通过以下代码来实现:
articles = models.Article.objects.filter(create_time__week_day=4)
8.time: 根据时间进行查找
如果要具体到秒,一般比较难匹配到,因为数据库中秒后面还有小数。可以使用区间的方式来进行查找。区间使用range
条件。比如想要获取17时/10分/27-28秒之间的文章,那么可以通过以下代码来实现:
from datetime import datetime,time
start_time = time(hour=17,minute=10,second=27)
end_time = time(hour=17,minute=10,second=28)
articles = models.Article.objects.filter(create_time__time__range=(start_time,end_time))
更多的关于时间的过滤,请参考Django官方文档:QuerySet API reference | Django documentation | Djangohttps://docs.djangoproject.com/en/2.1/ref/models/querysets/#range
9.isnull:根据值是否为空进行查找
# 查询title为空的
articles = models.Article.objects.filter(create_time__isnull=True)
# 查询title不为空的
articles = models.Article.objects.filter(create_time__isnull=False)
10.regex:正则表达式
articles = models.Article.objects.filter(title__regex=r"^三国")
articles = models.Article.objects.filter(title__iregex=r"^三国") # 不区分大小写
11.查找字段最大值
from django.db.models import Max
max = User_company.objects.aggregate(Max('nid'),Max('id'))
# {'nid__max': 9, 'id__max': 70}
查询集合并
qs1 = Pathway.objects.filter(label__name='x')
qs2 = Pathway.objects.filter(reaction__name='A + B >> C')
qs3 = Pathway.objects.filter(inputer__name='WeizhongTu')
# 合并到一起
qs = qs1 | qs2 | qs3
查询方法
all() | 查询所有内容 |
get() | 查询符合条件的返回模型类的对象符合条件的对象只能为一个,如果符合筛选条件的对象超过了一个或者没有一个都会抛出错误 |
filter() | 查询符合条件的数据 |
exclude() | 查询不符合条件的数据 |
order_by() | 对查询结果进行排序 |
reverse() | 对查询结果进行反转 |
count() | 查询数据的数量返回的数据是整数 输入参数,则为统计该列非空数量。 |
first() | 返回第一条数据,返回的数据是模型类的对象也可以用索引下标 |
last() | 返回最后一条数据返回的数据是模型类的对象不能用索引下标 [-1],ORM 没有逆序索引 |
exists() | 判断查询的结果 QuerySet 列表里是否有数据 |
values() | 查询部分字段的数据 返回的是 QuerySet 类型数据,类似于 list,里面不是模型类的对象,而是一个可迭代的字典序列,字典里的键是字段,值是数据。 |
values_list() | 查询部分字段的数据 返回的是 QuerySet 类型数据,类似于 list,里面不是模型类的对象,而是一个个元组,元组里放的是查询字段对应的数据。 |
distinct() | 对数据进行去重 |
select_related() | Goods.object.select_related('User').get(id=0) 同时查询外键,以减少多外键时的查询交数 |
only() | Blog.objects.filter(category='django').only('author','title') 仅查询指定列,提高查询速度 |
defer() | 仅查询指定字段以外的列,提高查询速度 |
其他
模型类转字典
__dict__
>>> Group.objects.get(id=1).__dict__
{'id': 1, 'name': 'GroupA', '_state': <django.db.models.base.ModelState object at 0x7f68612daef0>}
# 有缺陷 多个没用的_state字段,外键字段有问题
model_to_dict
>>> model_to_dict(Group.objects.get(id=1))
{'name': 'GroupA', 'id': 1}
>>>
>>> model_to_dict(User.objects.get(id=2))
{'leader': 1, 'is_active': True, 'username': 'ops-coffee@163.com', 'fullname': '运维咖啡吧', 'group': [<Group: GroupA>, <Group: GroupC>, <Group: GroupE>], 'id': 2}
这种方法能满足大部分的需求,且输出也较为合理,同时还有两个参数fields
和exclude
来配置输出的字段,例如:
>>> from django.forms import model_to_dict
>>> model_to_dict(User.objects.get(id=2), fields=['fullname','is_active'])
{'is_active': True, 'fullname': '运维咖啡吧'}
>>>
>>> model_to_dict(User.objects.get(id=2), exclude=['group','leader','id'])
{'fullname': '运维咖啡吧', 'is_active': True, 'username': 'ops-coffee@163.com'}
模型类转JSON字符串
from django.core.serializers import serialize
jsondata = serialize('json',data)
模板
模板中 字典key为变量
from django.template.defaulttags import register
@register.filter
def get_item(dictionary,key):
return dictionary.get(key)
{% for item in pageObj %}
<h7>{{ nameDict|get_item:item.cid }}</h7>
{% endfor %}
数据回滚
一、整体回滚
所有的数据库更新操作都会在一个事务中执行,如果事务中任何一个环节出现错误,都会回滚整个事务。
from django.db import transaction, DatabaseError
# open a transaction
@transaction.atomic #装饰器格式
def func_views(request):
do_something()
a = A() #实例化数据库模型
try:
a.save()
except DatabaseError:
pass
此方案整个view都会在事务之中,所有对数据库的操作都是原子性的。
from django.db import transaction, DatabaseError
def func_views(request):
try:
with transaction.atomic(): #上下文格式,可以在python代码的任何位置使用
a = A()
a.save()
#raise DatabaseError #测试用,检测是否能捕捉错误
except DatabaseError: # 自动回滚,不需要任何操作
pass
此方案比较灵活,事务可以在代码中的任意地方开启,对于事务开启前的数据库操作是必定会执行的,事务开启后的数据库操作一旦出现错误就会回滚。