Django ORM不完全操作
启动一个Django关联orm的项目
创建Django项目
用命令行创建Django项目,并且在里面创建默认的业务文件夹:
django-admin startproject mysite
cd mysite
python manage.py starapp app01
设置
-
创建数据
-
在Django项目文件夹的settings.py文件中添加
DATABASES={
'default':{
"ENGINE":'django.db.backends.mysql',
'NAME':'turbo1',
'USER':'root',
'PASSWORD':'root',
'HOST':'localhost',
'PORT':3306
}
- 在Django项目文件夹的
__init__.py
文件中,写入载入模块,供整个项目文件夹使用加载
import pymysql
pymysql.install_as_MySQLdb()
- 注册app,在Django项目文件夹中的
settings.py
中的INSTALLED_APPS中添加你创建的业务文件夹名称app01
编写orm语句,创建类
class UserGroup(models.Model):
"""
部门
没有主键的时候,会默认创建自增唯一非空主键
"""
title = models.CharField(max_length=32)
"""
models.UserGroup.objects.create(title='销售部')
# 添加数据
models.UserGroup.objects.filter(id=2).delete()
# 指定位置(.filter==where)删除数据
models.UserGroup.objects.filter(id=2).update(title='公关部')
# 指定位置更新替换数据库
group_list = models.UserGroup.objects.all()
# 通过赋值获取数据
group_one = models.UserGroup.objects.filter(id=1)
# 通过赋值和指定位置获取单个数据
group_list = models.UserGroup.objects.filter(id__gt=1)
# "__gt"====> ">"获取符合大于1的数据
group_list = models.UserGroup.objects.filter(id__lt=1)
# "__lt"====> "<"获取符合小于1的数据
"""
class UserInfo(models.Model):
"""
员工
"""
nid = models.BigAutoField(primary_key=True)
#创建主键(自增,主键)
user = models.CharField(max_length=32)
password = models.CharField(max_length=64)
#字符串类型,可以设置最大值
age = models.IntegerField(default=1)
# int类型,可以设置默认值
# ug_id
ug = models.ForeignKey("UserGroup",null=True)
# 外键的创建,因为外键一定是另一表的主键,所以只需指明表即可
通过语句获得的,都是queryset类型,一般都会在语句最后添加一个first,或者将获取的结果(看成一个list)前面添加一个[0]
,表示取第一个数据对象。
- 批量操作:
当我们获取到的筛选条件非常多的时候,我们可以将这些条件转换成一个字典格式,然后通过如下的语法一次性获得满足条件的数据:
create(name='xxxx',age=16......)
dic={name:'xxxx',age:16.....}
create(**dic)
#
models.xx.objects.filter(id).update(**dic)
#
models.xx.objects.filter(**(id:1,name:"xxxx"))
运行创建表命令
注意要在,项目文件夹下,运行manage.py makemigreations
H:\super1>python manage.py makemigrations
Migrations for 'app01':
app01\migrations\0003_classroom_student.py
- Create model ClassRoom
- Create model Student
H:\super1>python manage.py migrate
Operations to perform:
Apply all migrations: admin, app01, auth, contenttypes, sessions
Running migrations:
Applying app01.0003_classroom_student... OK
Django的ORM语法:
正向反向操作(Django-ORM重点语法)
获取Query Set
通常我们获得的QuerySet,是一个[obj,obj,obj]
的状态。这时候获取我们所需的值:
result = models.UserInfo.objects.all()
for obj in result:
print(obj.name,obj.age,obj.ut_id,obj.ut.title)
正向操作数据
在UserInfo表中,ut是外键(FK)字段
正向操作:
通过这个表的外键去获取外键关联表的信息
obj = models.UserInfo.objects.all().first()
print(obj.name,obj.age,obj.ut.title)
**那么这个操作对数据进行了几次请求?**
在obj获取值的时候,对数据库进行了一次请求,缓存了一个关于UserInfo的QuerySet。
但是在print的时候因为对外键ut关联表的title列数据又有了请求。所以就有了**两次**访问数据库
举个例子:
```python
q= models.UserInfo.objects.all()
for row in q:
print(row.name,row.ut.title)
上面的这个例子,向数据库发送了多少次请求?
len(q)+1
次
这种操作效率比较低,所以我们一般会在获取数据的时候,一次性获取想要的数据。方法就是在获取数据的时候,直接通过values
或者values_list
- 利用
.values
方法获取字典格式的数据
result = models.UserInfo.objects.all().values('id','name',"ut.title")
QuerySet[{'id':'xx','name':'xx',"ut.title":"xx"} ]
for row in result:
print(row)
- 利用
.values_list
方法获取元组格式的数据
result = models.UserInfo.objects.all().values_list('id','name',"ut.title")
QuerySet[(1,'f',"xxx"), ]
for row in result:
print(row)
反向操作数据
而在表UserType中, 并没有显示自己的某个值是别的表的外键,但是因为我们知道他有外键关联关系。所以通过表名小写_set.all()
------> 反向操作
PS: 一个用户类型下可以有很多用户
obj = models.UserType.objects.all().first()
# 获取所有用户类型列表
print('用户类型',obj.id,obj.title)
# 通过反向操作语法,获取关联外键表的信息,并且打印
for row in obj.userinfo_set.all():
print(row.name,row.age)
result = models.UserType.objects.all()
for item in result:
print(item.title,item.userinfo_set.filter(name='xx'))
一次性反向操作出所有外键关联表的数据:
obj= models.UserType.objects.all()
# for i in obj:
# print(i.id,'----',i.utype)
for i in obj:
for j in i.usertab_set.all():
print(j.uid,'---',j.uname,'---',j.uage)
获取多个数据时,如果首先获取的result是一个对象集,那么可以通过这个对象集进行跨表获取数据(通过外键获取外键关联表的数据。)
但如果语句筛选的时候,限制了字典或者是元组格式获取,就无法直接通过obj.FK.title
这种语法获取数据,而是需要特殊的语法,重新获取一个新的支持字典(元组)格式的对象集。
综上所述:
如果在获取对象阶段没有获取跨表数据,特殊格式获取的对象集无法完成跨表操作
- 列表格式
[obj,obj,obj,]
models.UserInfo.objects.all()
# 全选
models.UserInfo.objects.filter(id__gt=1)
# 条件选择
result = models.UserInfo.objects.all()
for item in result:
print(item.name,item.ut.title)
- 字典格式
[{id:1,name:fd},{id:1,name:fd},{id:1,name:fd},]
models.UserInfo.objects.all().values('id','name')
models.UserInfo.objects.filter(id__gt=1).values('id','name')
无法跨表
result = models.UserInfo.objects.all().values('id','name')
for item in result:
print(item['id'],item['name'])
跨表
result = models.UserInfo.objects.all().values('id','name',"ut__title")
for item in result:
print(item['id'],item['name'],item['ut__title'])
[(1,df),(2,'df')]
models.UserInfo.objects.all().values_list('id','name')
models.UserInfo.objects.filter(id__gt=1).values_list('id','name')
无法跨表
result = models.UserInfo.objects.all().values_list('id','name')
for item in result:
print(item[0],item[1])
跨表 __
result = models.UserInfo.objects.all().values_list('id','name',"ut__title")
for item in result:
print(item[0],item[1],item[2])
如何查看用ORM生成SQL语句
- 查看select语句
query
只能查看select语句,对其他更新保存的语句不能查看。换句话说,只有Queryset 有query方法
p = models.Usertab.objects.all().filter(uname="scott")
print(p.query)
- 查看UPDATE,INSERT语句
save
该方法会打印出所有执行过的sql语句
c = models.Usertab.objects.create(uname="goudan",age=18,ut_id=3)
print(c.save().query)
Django的ORM操作补充
- 排序order_by
# 正向排序
user_ist =models.Usertab.objects.all().order_by('uid')
# 反向排序
user_ist =models.Usertab.objects.all().order_by('-uid')
- 分组
.annotate 指定聚合条件
from django.db.models import Count
v= models.Usertab.objects.values('ut_id').annotate(xxx=Count('id'))
select Usertab.ut_id ,count(id) as xxx from usertab group by ut_id
二次筛选:在语句的后面放入.filter
,相当于having
。如果filter
字段在前面,就表示先筛选,再分组。
- 简单函数
from django.db.models import Count,Sum,Max,Min
具体含义就不说了,只要知道从哪个地方去导入就可以了
v=models.UserInfo.objects.values('ut_id').annotate(xxxx=Count('id'))
print(v.query)
# select ut_id,count("id") from UserInfo group by count("id")
v=models.UserInfo.objects.values('ut_id').annotate(xxxx=Count('id')).filter(xxxx__gt=2)
print(v.query)
# select ut_id,count("id") from UserInfo group by count('ID') having count("id")>2
v=models.UserInfo.objects.filter(id__gt=2).values('ut_id').annotate(xxxx=Count('id')).filter(xxxx__gt=2)
# select ut_id,count('id') where id>2 group by count('id') having count('id')>2
print(v.query)
- 过滤
__gt 大于
__lt 小于
__lte 小于等于
__gte 大于等于
__in 在……内[1,2,3,4]
__range 范围 []
__startswith 以……开始
__endswith 以……结尾
__contains 包含
.exlude(xx=xx) 不等于
F()----专门取对象中某列值的操作
from django.db.models import F
F()允许Django在未实际链接数据的情况下具有对数据库字段的值的引用。
通常情况下我们在更新数据时需要先从数据库里将原数据取出后方在内存里,然后编辑某些属性,最后提交。
models.UserInfo.objects.all().update(age=F("age")+1)
# 其实是Django模块帮你生成了一句语句:
# update UserInfo set ..... "age"="age"+1
# 其中....就表示这个表的其他列的数据(被重新赋一个原值),改变的只有被F()的age列
当Django程序中出现F()时,Django会使用SQL语句的方式取代标准的Python操作。
代码中不管 models.UserInfo.objects.age
的值是什么,Python都不曾获取过其值,python做的唯一的事情就是通过Django的F()函数创建了一条SQL语句然后执行而已。
需要注意的是在使用上述方法更新过数据之后需要重新加载数据来使数据库中的值与程序中的值对应:
order=models.UserInfo.objects.get(pk=order.pk)
或者使用更加简单的方法:
models.UserInfo.refresh_from_db()
参考:
http://www.tuicool.com/articles/u6FBRz
Q()----对对象的复杂查询
Q对象(django.db.models.Q)可以对关键字参数进行封装,从而更好地应用多个查询。可以组合使用 &(and)
,|(or)
,~(not)
操作符,当一个操作符是用于两个Q的对象,它产生一个新的Q对象。
-
Q对象(django.db.models.Q)可以对关键字参数进行封装,从而更好地应用多个查询
-
可以组合使用&,|操作符,当一个操作符是用于两个Q的对象,它产生一个新的Q对象。
-
Q对象可以用~操作符放在前面表示否定,也可允许否定与不否定形式的组合
models.UserInfo.objects.filter(id=1,name='root')
condition = {
'id':1,
'name': 'root'
}
models.UserInfo.objects.filter(**condition)
- Q使用有两种方式:对象方式,方法方式
from django.db.models import Q
# 作为方法调用Q()
models.UserInfo.objects.filter(Q(id__gt=1))
models.UserInfo.objects.filter(Q(id=8) | Q(id=2))
models.UserInfo.objects.filter(Q(id=8) & Q(id=2))
# 实例化一个Q对象,对这个对象进行参数赋值
q1 = Q()
q1.connector = 'OR'
# connector表示以下子条件的连接方式(关系)
q1.children.append(('id__gt', 1))
q1.children.append(('id', 10))
q1.children.append(('id', 9))
或(or)方式连接
q2 = Q()
q2.connector = 'OR'
q2.children.append(('c1', 1))
q2.children.append(('c1', 10))
q2.children.append(('c1', 9))
与(AND)方式连接
q3 = Q()
q3.connector = 'AND'
q3.children.append(('id', 1))
q3.children.append(('id', 2))
q2.add(q3,'OR')
对象方式使用Q(),调用上面设置的两个Q
con = Q()
con.add(q1, 'AND')
con.add(q2, 'AND')
讲条件封装在一个字典中,设置好条件的connector之后哦,用for循环构成多循环语句
condition_dict = {
'k1':[1,2,3,4],
'k2':[1,],
}
con = Q()
for k,v in condition_dict.items():
q = Q()
q.connector = 'OR'
for i in v:
q.children.append(('id', i))
con.add(q,'AND')
models.UserInfo.objects.filter(con)
extra
v = models.UserInfo.objects.all().extra(
select={
'n':"select count(1) from app01_usertype where id=%s or id=%s",
'm':"select count(1) from app01_usertype where id=%s or id=%s",
},
select_params=[1,2,3,4])
for obj in v:
print(obj.name,obj.id,obj.n)
models.UserInfo.objects.extra(
where=["id=1","name='alex'"]
)
models.UserInfo.objects.extra(
where=["id=1 or id=%s ","name=%s"],
params=[1,"alex"]
)
执行原生SQL
from django.db import connection, connections
cursor = connection.cursor() # cursor = connections['default'].cursor()
cursor.execute("""SELECT * from auth_user where id = %s""", [1])
row = cursor.fetchone()</pre>
Django的MTV(View)模式
Django中的View部分,就是如何用代码来与models中定义的字段进行交互。
与传统MVC分层定义略有不同的是,在Django 中,View的功能是对页面请求进行响应和逻辑控制,而页面内容的表示则由Django的Template模板来完成。我们可以把Django的View 理解为实现各种功能的Python函数,View负责接受URL配置文件urls.py中定义的URL转发并响应处理,当Django收到请求之后调用相 应的View函数来完成功能
目前只要知道我们通常用来把他里面的方法通过类继承的方式,进行二次编辑,用于不同method类型的和前端的数据交互(Ajax)。
CBV和FBV
创建model语句
class Boy(models.Model):
name=models.CharField(max_length=32)
pwd=models.CharField(max_length=16)
m=models.ManyToManyField('Girl')
# 不会在原相关表中有任何操作,但会直接生成一个关联表boy_m,但是由于生成的这张表并不是通过Django的ORM的class语句生成,所以我们无法直接通过ORM语句来操作第三张表。
# 但是我们可以间接地通过boy对象的m属性对他进行操作。
class Girl(models.Model):
name = models.CharField(max_length=32)
pwd = models.CharField(max_length=16)
class Love(models.Model):
b= models.ForeignKey('Boy')
g= models.ForeignKey('Girl')
# class Meta:
# unique_together=[('b','g')]
# # 联合唯一索引
分页组件
-
内置组件
-
自定义
路由系统
- 路由系统
-
静态
-
动态
-
别名
-
无别名
- 别名
- 根据别名返生成URL
伪静态
- orm操作
默认连接sqlite,在settings中修改为mysql
pymysql.install_as_mysqldb()
- 模板语言
-
.索引
-
母版
- block
url -> 函数
a. /login/ -> def login
b. /add-user/(\d+)/ -> def add_user(request,a1)
c. /add-user/(?P<a1>\d+)/ -> def add_user(request,a1)
PS:
终止符:
^edit$
伪静态
url(r'^edit/(\w+).html$', views.edit),
d. 路由分发
urls.py
url(r'^app01/', include('app01.urls')),
app01.urls.py
url(r'^index.html$', views.index),
e. /add-user/(\d+)/ -> def add_user(request,a1) name=n1
根据名称可以反向生成URL
1. 在Python代码中
from django.urls import reverse
v = reverse('n1',kwargs={'a1':1111})
print(v)
2.
url(r'^login/', views.login,name='m1')
{% url "m1" %}