一些有用的小东西:
- 有时在改数据库或render一个页面时会没有自动提示,参考:
- https://blog.csdn.net/cf313995/article/details/83117421
- return的快捷键,在返回时用 .re 回车即可
数据库中数据类型(大类)
- 字符串
- 数字
- 时间
字段
字段 | 意义 |
---|---|
null | 若为true,将空值(None)存在数据库中,默认false |
blank | 若为true,则允许该字段空白(""),默认false |
db_column | 如果不指定就是属性名 |
db_index | 若为true,则会在表中为此字段创建索引 |
default | 默认值 |
primary key | 该字段会成为模型的主键 |
unique | 这个字段在表中要有唯一值(用户名、邮箱等) |
Meta | 在Models定义的类中再定义Meta子类可以指定表名等信息 |
验证
class Person(models.Model):
name = models.CharField(max_length=16, db_column='Name', unique='True')
age = models.IntegerField(default=18, db_column='Age')
sex = models.BooleanField(default=False, db_column='Sex')
class Meta:
db_table = 'People'
结果如下:
objects
过滤器
可迭代
前两天用到了objects中的get、all,今天介绍下面几个方法:
- filter, 过滤符合条件的数据
- exclude, 返回不符合条件的数据
- order_by, 排序
- values, 将数据转换成字典,返回一个列表
单个数据
名称 | 意义 |
---|---|
get | 返回一个满足条件的对象,没有或找到多个都会触发异常,因此使用时要用try |
first | 返回查询集中第一个对象,和下面的last有时会获取到相同的对象(bug),解决办法是先手动order_by |
last | 返回查询集中最后一个对象 |
内置函数 | 意义 |
---|---|
count | 返回当前查询集中的对象个数,由于get毛病很多,因此查询信息(例如某个用户信息)时,虽然你知道结果唯一/不存在,但还是用count好一些,具体用法:先filter一下,然后对结果取count看看值是否为0 |
exists | 若查询集中有数据则返回true,上面count的用法使用exists也可以 |
切片
可以在查询后面直接写[x:y],表示取结果从下标2到下标4的数据,但要注意这和python中的切片不同
persons = Person.objects.all().order_by('age')[2:5]
all、filter、exclude等方法不会真正去查询数据库,只有当在迭代结果集或者获取单个对象属性的时候才会查数据库
可以在一起使用:filter.filter.exclude.x
一个例子(filter)
利用刚刚创建的Models来生成数据并查询数据:
def add_persons(request):
for i in range(0, 15):
person = Person()
flag = random.randrange(100)
person.name = 'caesar%d' % i
person.age = flag % 30
person.sex = flag % 2
person.save()
return HttpResponse('add success!')
def get_persons(request):
persons = Person.objects.filter(age__gt=18).filter(age__lt=25)
context = {
"persons": persons
}
return render(request, 'person_list.html', context=context)
html:
<h3>Person_List</h3>
<ul>
{% for person in persons %}
<li>姓名:{{ person.name }} ,年龄: {{ person.age }} ,性别: {{ person.sex }}</li>
{% endfor %}
</ul>
结果:
姓名:caesar8 ,年龄: 23 ,性别: True
姓名:caesar10 ,年龄: 19 ,性别: True
姓名:caesar11 ,年龄: 24 ,性别: False
姓名:caesar12 ,年龄: 20 ,性别: False
姓名:caesar13 ,年龄: 24 ,性别: False
注: filter(age__gt=18)是指找出age大于18的,
filter(age__lt=25)是找出age小于25的。
一个例子(order_by)
如果用all来查询,那么结果是从头到尾呈现的:
姓名:caesar0 ,年龄: 6 ,性别: False
姓名:caesar1 ,年龄: 7 ,性别: True
姓名:caesar2 ,年龄: 13 ,性别: True
姓名:caesar3 ,年龄: 13 ,性别: True
姓名:caesar4 ,年龄: 27 ,性别: True
姓名:caesar5 ,年龄: 26 ,性别: False
姓名:caesar6 ,年龄: 2 ,性别: False
姓名:caesar7 ,年龄: 5 ,性别: True
姓名:caesar8 ,年龄: 23 ,性别: True
姓名:caesar9 ,年龄: 18 ,性别: False
姓名:caesar10 ,年龄: 19 ,性别: True
姓名:caesar11 ,年龄: 24 ,性别: False
姓名:caesar12 ,年龄: 20 ,性别: False
姓名:caesar13 ,年龄: 24 ,性别: False
姓名:caesar14 ,年龄: 7 ,性别: True
姓名:peter ,年龄: 23 ,性别: True
姓名:Bob ,年龄: 18 ,性别: True
加了order_by之后:
persons = Person.objects.all().order_by('-id')
结果:
姓名:Bob ,年龄: 18 ,性别: True
姓名:peter ,年龄: 23 ,性别: True
姓名:caesar14 ,年龄: 7 ,性别: True
姓名:caesar13 ,年龄: 24 ,性别: False
姓名:caesar12 ,年龄: 20 ,性别: False
姓名:caesar11 ,年龄: 24 ,性别: False
姓名:caesar10 ,年龄: 19 ,性别: True
姓名:caesar9 ,年龄: 18 ,性别: False
姓名:caesar8 ,年龄: 23 ,性别: True
姓名:caesar7 ,年龄: 5 ,性别: True
姓名:caesar6 ,年龄: 2 ,性别: False
姓名:caesar5 ,年龄: 26 ,性别: False
姓名:caesar4 ,年龄: 27 ,性别: True
姓名:caesar3 ,年龄: 13 ,性别: True
姓名:caesar2 ,年龄: 13 ,性别: True
姓名:caesar1 ,年龄: 7 ,性别: True
姓名:caesar0 ,年龄: 6 ,性别: False
默认是按照id排序,如果想换成年龄,里面参数传’age’即可,若想降序,则’-age’。
一个例子(values)
对上面的代码稍作更改:
persons = Person.objects.all().order_by('age')
p_values = persons.values()
print(p_values)
结果:
<QuerySet [
{'id': 7, 'name': 'caesar6', 'age': 2, 'sex': False},
{'id': 8, 'name': 'caesar7', 'age': 5, 'sex': True},
...,]>
可以看到这个形式类似json,方便以后做转换
查询条件
格式:
- 属性__运算符=值
例如前面提到的:
- age__gt=17 : 年龄>17
- age__lt=25 : 年龄<25
以及:
- in : 在某一集合中
- contains : like
- startswith/endswith : 以…开始/结束
- exact :
对时间的查询:
- date = Date.objects.filter(time__year=2020) 查询年份
- date = Date.objects.filter(time__month=10) 查询月份
但要注意查月份由于时区的问题会导致无法输出结果,解决办法是:- 关闭Django中自定义的时区(settings->USE_TZ = True设为False即可)
- 在数据库中创建对应的时区表(不太好)
models.py
class Date(models.Model):
time = models.DateTimeField(auto_now_add=True)
num = models.CharField(max_length=16, unique=True)
views.py
def get_date(request):
date = Date.objects.filter(time__month=6)
for i in date:
print(i.num)
创建对象
之前是用a = A()的形式创建对象,还可以用下面的方式:
-
person = Person.objects.create(name=‘peter’, age=‘23’, sex=‘1’)
-
定义一个类方法,让这个方法返回对象:
@classmethod def create(cls, name, age=18, sex=1): return cls(name=name, age=age, sex=sex)
然后在views中:
person = Person.create('Bob')
当然也可以用类直接创建对象:Person(name=x, age=y, sex=z),但这样需要将参数全都写上(版本的问题?不太清楚),因此用类方法的形式可以设置默认参数,并且可以对传过来的参数进行处理。
级联查询
现有学生和班级两个表,学生的外键是班级的主键,想查询某个学生所在的班级:
grades = Grade.objects.filter(student__s_name='xxx')
聚合函数
在sql中,Max、Avg、Sum等函数可由如下形式实现:
# 注意导包
from django.db.models import Max, Min, Sum, Avg, Count
students.object.aggregate(Max('age'))
F对象
用于比较表中的两个字段
name | boy | girl |
---|---|---|
A | 10 | 5 |
B | 12 | 30 |
C | 30 | 20 |
现在想看看男比女少的公司都有哪些,代码如下:
companies = Company.objects.filter(boy__lt=F('girl'))
for company in companies:
print(company.num)
结果:
B
如果想看看男生比女生多超过8个人的公司:
companies = Company.objects.filter(girl__lt=F('boy')-8)
Q对象
可以支持逻辑运算
如果想获取男生多于1且女生多于5的公司,可以这么写:
res = Company.objects.filter(boy__gt=1).filter(girl__gt=5)
使用Q对象之后:
companies = Company.objects.filter(Q(boy__gt=1) & Q(girl__gt=1))
可以重写Django方法
有如下model:
class Animal(models.Model):
a_name = models.CharField(max_length=16)
is_delete = models.BooleanField(default=False)
数据是:
cat,0
cow,1
dog,0
现在想找is_delete为False的数据,可以这么写:
animals = Animal.objects.filter(is_delete=False)
这个objects是Manager类型的一个对象,是隐性的(显示的调用它会报错;a_m=models.Manager()会报错),如果没有指定管理器,Django会自动创建模型管理器;但是如果自定义了模型管理器,那么objects就不会存在,也就是不会为我们生成模型管理器。
所以我们能不能实现这样一个东西,即调用all方法返回的就是is_delete=False的东西?
通过查看all()方法源码,发现其实调用的是get_queryset,因此重写这个方法即可:
class AnimalManager(models.Manager):
def get_queryset(self):
return super(AnimalManager, self).get_queryset().filter(is_delete=False)
class Animal(models.Model):
a_name = models.CharField(max_length=16)
is_delete = models.BooleanField(default=False)
a_m = AnimalManager()
views:
animals = Animal.a_m.all()
for animal in animals:
print(animal.a_name)
return HttpResponse('查询动物成功!')
可以看到,Animal.a_m.all()这句就能把is_delete=False的数据给找出来而不必再每次都写filter。
当然,也可以把a_m改成objects这样在views中根本没改变代码就实现了功能,如果想恢复all本来的功能只需要把a_m = AnimalManager()注释掉即可。