导图社区 Django开发流程
Django开发流程,包括request常用用法、操作数据的几种方式、元类重写的实例(元类type)三部分内容。
编辑于2022-02-09 11:50:48Django开发
request常用用法
request.user获取当前用户
request.method获取请求方式
request.COOKIES获取COOKIE
request.META获取请求头
get和post请求参数的获取,request.GET,request.POST,使用get(key,value)形式获取
request.body获取json参数
获取文件request.FILES,设置文件FILES.get(key)(二进制加.read()函数)
操作数据的几种方式
数据迁移
makemigrations
每次运行一次就会生成一个000x_initial.py文件
migrate并不会对同一个.py文件重复执行,因为Django执行时会将该文件的执行记录保存在数据表django_migrations中;若数据库中已经做出了改变,则不会执行。
如果需要重复执行该.py文件,就需要在数据表里删除相应的文件执行记录(但不建议这样做,会出现异常,比如数据表已经存在了,在次执行就会报错"table 'xxx' already exists"异常)
注意事项:新增模型字段将属性null和blank设为true或者设为为模型字段设置默认值(default),否则执行makemigrations指令会提示字段修复信息
sqlmigrate指令(但并不会执行sql指令)
python manage.py sqlmigrate 应用名 脚本文件名
作用:根据迁移文件内容输出相应的SQL语句
应用场景:写脚本来批量处理数据库时会很有用
数据的导入导出
loaddata来实现数据的导入操作
dumpdata来实现数据的导出操作
子主题
Django数据表创建多对多的关系时候只需要创建两个表即可,第三个多对多的表会自动生成
一对一:OneToOneField
一对多:ForeignKey
多对多:ManyToManyField
数据表操作
新增:由模型实例化对象调用内置方式实现数据新增,比如单数据新增调用create,查询与新增调用get_or_create(数据存在则插入,不存在则不插入),修改与新增调用update_or_create(数据存在则更新否则新增),批量新增调用bulk_create
在实例化时直接设置属性值
In [15]: v = Vocation(job='Python后端开发工程师',title='后端开发工程师',payment=0,name_id=4) In [16]: v.save() [2022-01-23 22:16:21,152] (0.056) INSERT INTO "index_vocation" ("job", "title", "payment", "name_id") VALUES ('Python后端开发工程师', ' 后端开发工程师', 0, 4); args=['Python后端开发工程师', '后端开发工程师', 0, 4]
mdele_name.objects.create(字段名1='xx',字段名2='xx')
使用字典的格式插入,d=dict((字段名='xx',));然后再mdele_name.objects.create(**d)
objects.get_or_create()去重插入(插入的数据不在数据表中则插入,只要有一个数据不同就可以插入)
bulk_update()中插入的是一个将数据对象,该对象类型为列表或元组
修改:修改之前必须要执行一次数据查询操作,再对查询结果进行删除操作,常用方法有:模型实例化,update方法和批量更新bulk_update
使用update_or_create(**d,defaults={})方法既可以新增也可以修改,(分两种情况:主键相同,则修改其他字段;第二种主键必须唯一,所以相同的主键插入数据就会报错)修改的内容以字典格式传递给参数defaults即可(只能在是用**d传入字典格式的时候才可以使用)很短板,看id的(主键),id不同才能更新或者修改,id相同都不可以
先查询后修改;v = name.objects.get(id=xxx) v.xxx= xxx v.save() # 即可
批量更新一条或多条数据,查询方法使用filter
在filter后加个.update(字段值=xx)
修改数据也可以使用字典,upadte(**d);不使用查询方法,默认对全表的数据进行更新
Mysql中可能没有,使用F方法实现数据的自增或自减(主键设置的话不能自增或自减,确保主键唯一)
v = Vocation.objects.filter(job='uu_aa') # 将payment字段整体加1 v.update(payment=F('payment')+1) # Out[50]: 2
In [28]: v = Vocation.objects.filter(payment=10000).update(payment=F('payment')+1) [2022-01-23 22:31:50,986] (0.056) UPDATE "index_vocation" SET "payment" = ("index_vocation"."payment" + 1) WHERE "index_vocation"."payment" = 10000; args=(1, 10000)
删除:删除之前必须要执行一次数据查询操作,在对查询结果进行删除操作,若删除的数据设有外键字段,则删除结果由外键的删除模式决定
删除表中全部数据 Vocation.objects.all().delete()
删除一条id为1的数据(get请求返回的对象只能是一个) Vocation.objects.get(id=1).delete() # (1, {'index.Vocation': 1})
删除多条数据 Vocation.objects.filter(job='uu_aa').delete() # (2, {'index.Vocation': 2})
删除的数据带有外键字段,则删除结果由外键的删除模式决定(应该只懂CASCADE 级联)
CASCADE 级联删除。Django 模拟 SQL 约束 ON DELETE CASCADE 的行为,并删除包含 ForeignKey 的对象(删除多对一中的一(一般为主键),则对应的多关联的数据也删除,删除多中的数据,)
PROTECT 通过提高ProtectedError的子类来 防止删除引用的对象(删除数据中包含外键和其他关联的数据,就会删除失败)
SET_NULL设置为ForeignKey空;这只有在null是 时才有可能 True(null属性必须为True才可以删除,否则提示异常)
SET_DEFAULT删除该数据并将ForeignKey设为默认值
set模式执行数据删除并把其他数据表的外键字段关联到其他数据
DO_NOTHING不采取任何处理,删除结果由数据库的删除模式决定
单表查询
查询时候可以使用query.__str__()属性来打印出querysel实例化对象的sql语句(这个要返回的是查询集才可以,也就是带有QuerySet,get方法就不可以,它返回的是单个对象,不是查询集)(前面方法不管用,还是在Django中设置日志比较好)
<QuerySet [<Person: Lucy>]>
and查询和or查询
## SQL的and查询主要在filter里面添加多个查询条件,在django中表示用逗号(,)隔开即可 ## SQL的or查询需要引入Q,编写格式:Q(filter=value)|Q(filter=value) ### 多个Q之间使用'|'隔开 ##SQL:select * from index_vocation where job='网站设计' or id=9 from django.db.models import Q v = Vocation.objects.filter(Q(job='网站设计' )|Q(id=9)) v # ` <QuerySet [<Vocation: 3>]>`
截取前几条数据,或者按什么排序截取前几条数据可以采用列表的切片操作:[:3]截取前3条数据
Vocation.objects.all()[:3]
查询某个字段使用values方法,数据以列表返回,列表元素以字典表示
Vocation.objects.values('job')
使用get查询返回个单独的对象(多个查询结果的话就报错返回超过一个对象),filter返回个查询集(QuerySet),all()也是个QuerySet,但他一般是全表查询
聚合函数查询;使用values或者filter提取某些数据,然后再之后采用聚合函数对某些关键字进行聚合(Sum和Avg一定要和annotate一起使用,但分组对象还是values()的字段)
求和需要从django.db.models模块中导入Sum(S大写)
v = Vocation.objects.values('job').annotate(Sum('id'))
求平均值需要从django.db.models模块中导入Avg(A大写)
统计查询数据量 count方法(之后不需要传参数),或者使用从django.db.models模块中导入Count(C大写)
v = Vocation.objects.filter(job='网站设计').count()
v = Vocation.objects.aggregate(id_count=Count('id'))
aggregate是计算某个字段的值并只返回计算结果,常和聚合函数一起使用
无法将aggregate与annotate一起使用
不等于查询使用exclude或者~Q
v = Vocation.objects.filter(~Q(job='网站设计'))
v = Vocation.objects.exclude(job='网站设计')
去重查询sql中是distinct,但在Django中无需设置参数,返回的结果会根据values设置的字段执行(在末尾加distinct)
v = Vocation.objects.values('job').filter(job='网站设计').distinct()
分组与排序
annotate分组(该括号中的分组对象是什么),在Django中如果不设置values,则默认对主键进行分组
子主题
并集交集差
使用union()方法 类似于SQL的UNION
intersection求两次查询的交集,类似于SQL的INTERSECT
difference 差集 以前者为目标数据,返回也是前者去除共同数据之后的数据 SQL中的EXCEPT
多表查询
一对多情况
正向查询(从多表中查询一表的数据):即通过外键字段去查询关联字段的数据
In [65]: v = Vocation.objects.filter(id=2).first()# 多表 In [66]: v Out[66]: <Vocation: 2> ## 通过外键name去查询模型PersonInfo所对应的数据 In [67]: v.name.hireDate # hireDate是一表字段 Out[67]: datetime.date(2018, 9, 18)
在查询条件中(get或filter中)使用正向查询或者反向查询,查询两者关联的字段使用__双下划线链接
正向查询:(V开头的是1,p开头的是多);v = Vocation.objects.filter(name__name="tim").first() 前面的name是V中的name,后面的name是p中的name(即两者字段相同) 然后再通过外键关联查询P表符合改条件的数据: v.name.hireDate
还有个反向查询挺复杂的(反正记得调用属性字段的话要单个对象,而不能是查询集queryset)
无论正向查询还是反向查询都需要在数据库中执行两次SQL查询,正向查询通过外键字段(在查询条件中使用了外键,他就直接返回了通过外键查询的数据了),反向查询通过外键字段中设置的related_name参数字段值
反向查询:通过一查询到与他关联到的模型多的数据
设置了外键字段的ondelete属性为related_set
In [76]: p = PersonInfo.objects.filter(id=4).first() In [77]: v= p.name.first() # name是额外再外键ForeignKey()括号中除了ondelete之外的参数related_name In [78]: v Out[78]: <Vocation: 3>
并没有设置:
In [68]: p = PersonInfo.objects.filter(id=2).first() In [69]: p Out[69]: <PersonInfo: Tim> # 方法一 #vocation_set的返回值为queryset对象,即查询结果 # vocation_set为模型Vocation的名称小写 # 模型Vocation的外键字段name不能设置参数related_name # 若设置参数related_name,则无法使用vocation_set In [70]: v= p.vocation_set.first()
执行一次SQL查询就可以的语句 :
select_related方法使用sql的join语句进行优化的
反向查询(left join):反正还是要使用到related_name字段参数值
p = PersonInfo.objects.select_related('personinfo').values('name','personinfo__payment') # 查询一表中各个name字段对应多表中 的工资;values()中为查询的字段数据,多表中的数据用personinfo__ 关联
正向查询 (inner join):需要用到外键
v = Vocation.objects.select_related('name').values('name','name__age')
多表查询结果一定要为一个对象才可以(表名在前,如后面的Person)
对象的样子:In [191]: P = Person.objects.select_related('living__province').get(name='Lucy') In [192]: POut[192]: <Person: Lucy> In [193]: P.living.province Out[193]: <Province: 浙江省>
非对象:In [188]: P = Person.objects.select_related('living__province').values('name','living__province').filter(name='Tom').first() In [189]: P Out[189]: {'name': 'Tom', 'living__province': 2}
子主题
prefetch_related
多对多查询数据
In [220]: p = Program.objects.prefetch_related('performer').filter(name='喜洋洋').first() In [222]: p.performer.all() # 从多对多表中提取对应的多对多数据 Out[222]: <QuerySet [<Performer: Lily>, <Performer: Lilei>, <Performer: Tom>]>
子主题
一对一
子主题
子主题
子主题
子主题
子主题
执行SQL语句
extra:6个可选参数
where:设置查询条件,%s 是类似于格式化字符串
Vocation.objects.extra(where=['job=%s'],params=['文员'])
params:该参数为where提供数值
select:添加新的查询字段,即新增模型之外的字段(这个作用不知道,既然不存在模型之中,为什么还要新增)
v = Vocation.objects.extra(select={'seat':'%s'},select_params=['seatInfo'])
select_params:如果select设置了字符格式化%s,那么该参数为select提供数值
tables:连接其他数据表,实现多表查询
v = Vocation.objects.extra(tables=['index_personinfo'])
为什么这样返回5个数据相同的数据,难道是由于select了5个字段,并不是这个,反正弄不懂具体查询的是什么
In [238]: v = Vocation.objects.extra(tables=['index_personinfo'],where=['index_vocation.id=1'])
In [239]: v
Out[239]: [2022-01-24 13:19:23,032] (0.000) SELECT "index_vocation"."id", "index_vocation"."job", "index_vocation"."title", "index_vocation"."payment", "index_vocation"."name_id" FROM "index_vocation" , "index_personinfo" WHERE (index_vocation.id=1) LIMIT 21; args=()
<QuerySet [<Vocation: 1>, <Vocation: 1>, <Vocation: 1>, <Vocation: 1>, <Vocation: 1>, <Vocation: 1>]>
order_by:设置数据的排序方式,默认按照主键升序排序
raw :一个必选参数sql语句
必选:raw_query;sql语句;可他返回的是个查询集,需要用括号提取数据
In [259]: v = PersonInfo.objects.raw('select * from index_personinfo where id=1') In [260]: v Out[260]: <RawQuerySet: select * from index_personinfo where id=1>
parames:如果raw_quert设置字符串格式化%s,那么该参数为raw_quert提供值
translations:为查询的字段设置别名
using:数据库对象,即Django所连接的数据库
execute:需要借助第三方模块实现数据库连接,不用经过ORM框架
子主题
实现数据库事务
extra:只能实现数据库查询
raw :只能实现数据库查询
execute:无需经过ORM框架处理,能够执行所有的SQL语句
transaction方法中定义了Django的数据库事务
atomic() :在视图函数视图类中使用事务 savepoint():开启事务 savepoint_rollback():回滚事务 savepoint_commit():提交事务
使函数支持事务操作
(放在函数上)@transaction.atomic # 使函数支持事务操作
(放在函数内,具体语句之前)with transaction.atomic(): # with模块的作用和装饰器一样
在开启事务之前必须实例化一个事务对象;sid = transaction.savepoint()
多数据库的连接
配置多数据库
新建个dbRouter.py文件编写类DbAppsRouter,然后再settings.py文件中的配饰属性DATABASES中添加数据库信息即可;还需配置
# 新增dbRouter.py文件编写类DbAppsRouter DATABASE_ROUTERS = ['MyDjango.dbRouter.DbAppsRouter'] # 指向新编写的文件的类 DATABASE_APPS_MAPPING = { }# 设置数据库与项目应用的映射关系
在模型中设置所属app名(不太重要)
# 设置模型所属的App,从而在相应的数据库里生成数据表 # 如不设置app_label,则默认当前文件所在的App app_label = "index" # 将模型归属于某个应用
执行迁移
python manage.py migrate s --database==数据库名 (这里的数据库名是DATABASE_APPS_MAPPING参数中键值对的键,如默认的一般是default)
子主题
元类重写的实例(元类type)
继承类需指定元类的话需要加metaclass=重写的元类名
重写元类即继承元类type,然后重写它的__cls__方法即可(一般四个参数,self重写类名,name,bases,attrs存放属性和方法)
实例的话就是:ORM框架
orm框架是通过继承和修改元类type来实现的
子主题
主题
主题
rustful
前后端分离
表单与模型