django models
---恢复内容开始---
一:Models
ORM(对象关系映射):
- 优点:使用ORM,与数据库交互变得更简单,不用考虑麻烦的SQL语句。从而实现快速开发
- 缺点:
性能有所牺牲。但能通过缓存 或 延迟加载等来减轻这个问题,效果不错
复杂查询不擅长,但ORM支持 raw sql(自己写原生数据库语句)
表创建(模型):
- 学校模型
- 教师模型
- 教师详细信息模型
- 班级模型
一个学校有多个班级,多个班级对应一个学校 :一对多关系(foreign key)
一个老师对应一个教师详细信息 :一对一关系(OneToOneField)
多个教师对应多个班级,多个班级对应多个老师 : 多对多关系(ManyToManyField)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from django.db import models # Create your models here. class School(models.Model): """ 学校表 """ name = models.CharField(max_length=30, verbose_name='名称') site = models.CharField(max_length=30, verbose_name='地址') city = models.CharField(max_length=30, verbose_name='城市') province = models.CharField(max_length=30, verbose_name='省份') Grade_Class = models.ForeignKey('Grade') # 学校和班级的关系:一对多,一个学校有多个班级 class Meta: verbose_name = '学校' verbose_name_plural = verbose_name def __str__(self): return self.name class Teacher(models.Model): """ 教师表 """ name = models.CharField(max_length=30) def __str__(self): return self.name class TeacherInfo(models.Model): """教师信息表""" choices = ((0, '男'), (1, '女')) sex = models.BooleanField(max_length=1, choices=choices) email = models.EmailField() site = models.CharField(max_length=30) teacher = models.OneToOneField('Teacher') # 教师与教师详细信息的关联关系:一对一 class Grade(models.Model): """ 班级表 """ name = models.CharField(max_length=30) number = models.IntegerField() # 教师和班级的关系:多对多,多个老师对应多个班级,多个班级对应多个老师 teacher = models.ManyToManyField('Teacher') def __str__(self): return self.name
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1.CharField 字符串字段,用于较短的字符串 CharField 要求必须有一个参数 max_length ,用于从数据库和Django校验层限制该字段所允许的最大字符数 2.IntegerField 保存一个整数 3.FloatField 一个浮点数,必须提供两个参数: 1.参数max_digits 总位数() 2.描述(decimal_places 小数位数) 举例来说, 要保存最大值为 999 (小数点后保存2位),你要这样定义字段: # # models.FloatField(..., max_digits=5, decimal_places=2) # 要保存最大值一百万(小数点后保存10位)的话,你要这样定义: # # models.FloatField(..., max_digits=19, decimal_places=10) # admin 用一个文本框(<input type="text">)表示该字段保存的数据. 4.AutoField # 一个 IntegerField, 添加记录时它会自动增长. 你通常不需要直接使用这个字段; # 自定义一个主键:my_id=models.AutoField(primary_key=True) # 如果你不指定主键的话,系统会自动添加一个主键字段到你的 model. 5.BooleanField # A true/false field. admin 用 checkbox 来表示此类字段. 6.TextField # 一个容量很大的文本字段. # admin 用一个 <textarea> (文本区域)表示该字段数据.(一个多行编辑框). 7.EmailField # 一个带有检查Email合法性的 CharField,不接受 maxlength 参数. 8.DateField # 一个日期字段. 共有下列额外的可选参数: # Argument 描述 # auto_now 当对象被保存时,自动将该字段的值设置为当前时间.通常用于表示 "last-modified" 时间戳. # auto_now_add 当对象首次被创建时,自动将该字段的值设置为当前时间.通常用于表示对象创建时间. #(仅仅在admin中有意义...) 9.DateTimeField # 一个日期时间字段. 类似 DateField 支持同样的附加选项. 10.ImageField # 类似 FileField, 不过要校验上传对象是否是一个合法图片.#它有两个可选参数:height_field和width_field, # 如果提供这两个参数,则图片将按提供的高度和宽度规格保存. 11.FieldField # 一个文件上传字段. #要求一个必须有的参数: upload_to, 一个用于保存上载文件的本地文件系统路径. 这个路径必须包含 strftime #formatting, #该格式将被上载文件的 date/time #替换(so that uploaded files don't fill up the given directory). # admin 用一个<input type="file">部件表示该字段保存的数据(一个文件上传部件) . #注意:在一个 model 中使用 FileField 或 ImageField 需要以下步骤: #(1)在你的 settings 文件中, 定义一个完整路径给 MEDIA_ROOT 以便让 Django在此处保存上传文件. # (出于性能考虑,这些文件并不保存到数据库.) 定义MEDIA_URL 作为该目录的公共 URL. 要确保该目录对 # WEB服务器用户帐号是可写的. #(2) 在你的 model 中添加 FileField 或 ImageField, 并确保定义了 upload_to 选项,以告诉 Django # 使用 MEDIA_ROOT 的哪个子目录保存上传文件.你的数据库中要保存的只是文件的路径(相对于 MEDIA_ROOT). # 出于习惯你一定很想使用 Django 提供的 get_<#fieldname>_url 函数.举例来说,如果你的 ImageField # 叫作 mug_shot, 你就可以在模板中以 {{ object.#get_mug_shot_url }} 这样的方式得到图像的绝对路径. 12.URLField # 用于保存 URL. 若 verify_exists 参数为 True (默认), 给定的 URL 会预先检查是否存在( 即URL是否被有效装入且 # 没有返回404响应). # admin 用一个 <input type="text"> 文本框表示该字段保存的数据(一个单行编辑框) 13.NullBooleanField # 类似 BooleanField, 不过允许 NULL 作为其中一个选项. 推荐使用这个字段而不要用 BooleanField 加 null=True 选项 # admin 用一个选择框 <select> (三个可选择的值: "Unknown", "Yes" 和 "No" ) 来表示这种字段数据. 14.SlugField # "Slug" 是一个报纸术语. slug 是某个东西的小小标记(短签), 只包含字母,数字,下划线和连字符.#它们通常用于URLs # 若你使用 Django 开发版本,你可以指定 maxlength. 若 maxlength 未指定, Django 会使用默认长度: 50. #在 # 以前的 Django 版本,没有任何办法改变50 这个长度. # 这暗示了 db_index=True. # 它接受一个额外的参数: prepopulate_from, which is a list of fields from which to auto-#populate # the slug, via JavaScript,in the object's admin form: models.SlugField # (prepopulate_from=("pre_name", "name"))prepopulate_from 不接受 DateTimeFields. 15.XMLField #一个校验值是否为合法XML的 TextField,必须提供参数: schema_path, 它是一个用来校验文本的 RelaxNG schema #的文件系统路径. 16.FieldPathField # 可选项目为某个特定目录下的文件名. 支持三个特殊的参数, 其中第一个是必须提供的. # 参数 描述 # path 必需参数. 一个目录的绝对文件系统路径. FilePathField 据此得到可选项目. # Example: "/home/images". # match 可选参数. 一个正则表达式, 作为一个字符串, FilePathField 将使用它过滤文件名. # 注意这个正则表达式只会应用到 base filename 而不是 # 路径全名. Example: "foo.*\.txt^", 将匹配文件 foo23.txt 却不匹配 bar.txt 或 foo23.gif. # recursive可选参数.要么 True 要么 False. 默认值是 False. 是否包括 path 下面的全部子目录. # 这三个参数可以同时使用. # match 仅应用于 base filename, 而不是路径全名. 那么,这个例子: # FilePathField(path="/home/images", match="foo.*", recursive=True) # ...会匹配 /home/images/foo.gif 而不匹配 /home/images/foo/bar.gif 17.IPAddressField # 一个字符串形式的 IP 地址, (i.e. "24.124.1.30").
Filed参数说明:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1. null : 数据库中字段是否可以为空 2. blank: django的 Admin 中添加数据时是否可允许空值 3. default:设定缺省值 4. editable:如果为假,admin模式下将不能改写。缺省为真 5. primary_key:设置主键,如果没有设置django创建表时会自动加上: id = meta.AutoField('ID', primary_key=True) primary_key=True implies blank=False, null=False and unique=True. Only one primary key is allowed on an object. 6. unique:数据唯一 7. verbose_name Admin中字段的显示名称 8. validator_list:有效性检查。非有效产生 django.core.validators.ValidationError 错误 9. db_column,db_index 如果为真将为此字段创建索引 10. choices:一个用来选择值的2维元组。第一个值是实际存储的值,第二个用来方便进行选择。 如SEX_CHOICES= (( ‘F’,'Female’),(‘M’,'Male’),) gender = models.CharField(max_length=2,choices = SEX_CHOICES)
模型操作(增删改查):
--------------------------------------------------------- 增----------------------------------------------------------
import os, django
# 外部调用 Django models
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "DjangoTest.settings")
django.setup()
from app01.models import *
import sys
# create方式
Teacher.objects.create(name='王哲学')
Teacher.objects.create(**{'name':'张三'})
# save 方式
t = Teacher(name='李四')
t.save() #保存
t = Teacher()
t.name = '陈建国'
t.save()
一对多,多对多关系的数据写入:
一对多:
一个学校有多个班级,school表 关联着 班级表(Grade)的行对象
g = Grade.objects.create(name='三年级一班', number=20)
School.objects.create(name='若智小学',
site='北京二环死胡同288',
city='北京市',
province='北京市',
Grade_Class=g, # 直接指定对象
# 或者直接指定id
#Grade_Class=2, # 相当于将Grade表的ID=2的行对象进行关联
)
多对多:
一个班级可以有多个老师,多个老师可以对应多个班级。在ORM里,如果有多对多关系的模型,那么两表之间会再自动创建一张新表
![](https://images2018.cnblogs.com/blog/1331204/201807/1331204-20180731212535648-479293642.png)
# 方式1:
teacher1 = Teacher.objects.get(id=1)
teacher2 = Teacher.objects.filter(name='李四')[0]
grade = Grade.objects.get(id=1)
grade.teacher.add(teacher1, teacher2)
#方式2 正向关联:
一个班级去关联多个老师, 以 班级 这个对象为中心进行关联教师
teacher1 = Teacher.objects.filter(id__gt=1) # 返回Teacher表ID大于1的所有行对象,返回的是列表集合
grade = Grade.objects.filter(id=1)[0] # 返回Grade表id=1的行对象
grade.teacher.add(*teacher1) # 正向关联
grade.teacher.remove(*teacher1) # 删除
#方式3 反向关联:
1个老师去关联多个班级,以 教师 这个对象为中心进行关联班级
teacher1 = Teacher.objects.filter(id=1)[0]
grade = Grade.objects.filter(id__gt=1)
teacher1.grade_set.add(*grade)
teacher1.grade_set.remove(*grade)### 删除
反向关联的 表名_set 比较特殊,能通过 teacher 表 反向找到 grade 表,这是Django已经定义好的
****两表中,ManyToMany 字段定义在谁的那一端,谁就是正向,否则就是反向
--------------------------------------------------------- 删----------------------------------------------------------
Teacher.objects.filter(id=1).delete() # 删除ID=1的老师
Django默认级联删除,虽然上面看起来删除了1条信息,但实际上,关联了它的表的行对象数据也会被删除
多对多关系删除:
# 正向删除
grade = Grade.objects.filter(id=1)[0]
grade.teacher.clear() # 删除第三张中的与 Grade表中 id=1 的所有关联数据
------------------------
grade = Grade.objects.filter(id=2)[0]
teacher = Teacher.objects.filter(id__gt=1)
grade.teacher.remove(*teacher)
# 反向
teacher = Teacher.objects.filter(id=2)[0]
teacher.grade_set.clear()
--------------------------------------------------------- 改----------------------------------------------------------
# 方式1
teacher = Teacher.objects.get(id=2) teacher.name = '铁蛋' teacher.save()
# 方式2
Teacher.objects.filter(id=2).update(name='汤姆')
多对多修改:
grade = Grade.objects.filter(id=2)[0]
teacher = Teacher.objects.filter(id__gt=1)
grade.teacher.add(*teacher)
--------------------------------------------------------- 查----------------------------------------------------------
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 查询相关API: # <1>filter(**kwargs): 它包含了与所给筛选条件相匹配的对象 # <2>all(): 查询所有结果 # <3>get(**kwargs): 返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。 #-----------下面的方法都是对查询的结果再进行处理:比如 objects.filter.values()-------- # <4>values(*field): 返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列 model的实例化对象,而是一个可迭代的字典序列 # <5>exclude(**kwargs): 它包含了与所给筛选条件不匹配的对象 # <6>order_by(*field): 对查询结果排序 # <7>reverse(): 对查询结果反向排序 # <8>distinct(): 从返回结果中剔除重复纪录 # <9>values_list(*field): 它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列 # <10>count(): 返回数据库中匹配查询(QuerySet)的对象数量。 # <11>first(): 返回第一条记录 # <12>last(): 返回最后一条记录 # <13>exists(): 如果QuerySet包含数据,就返回True,否则返回False。
惰性机制:
在执行 teacher.objects.all() 和 teacher.objects.filter() 时,只是返回结果集对象,并不是立即执行sql,当调用QuerySet时才会执行
QuerySet 特点:
# 可迭代
# 可切片
t = Teacher.objects.all() #[obj1,obj2,obj3,obj4] ,返回一个QuerySet对象
for i in t:
print(i)
t[1]
t[1:5]
t[::-1]
QuerySet高效使用
1.QuerySet是惰性的
如:teacher = Teacher.object.filter(id=1) 在执行这样的代码时,它只是生成了数据库语句,但并没有执行,只要调用的时候才执行;
2.真正地在数据库里执行
如: print(teacher)
或者: for t in teacher:
print(t)
或者: if teacher:
print('xxx')
3.QuerySet 是有 cache(缓存) 的
当遍历或者调用QuerySet时,所有事先生成的数据库语句会在数据库执行,然后转换成Django的model.
这些model会被保存在QuerySet内置的cache里。当你再次遍历QuerSet时,无需重复执行数据库语句
4.如果查询量过大(QuerySet集合过大),cache会有可能不足
如果查询量过大,一次性载入内存里非常浪费,要避免这个问题,可以使用 迭代器(iterator)的方式来获取数据,处理完数据就丢弃
teacher = Teacher.objects.all().iterator() # iterator() 可以一次取少量数据,节省内存,用一次取一次
for t in teacher:
print(t.name)
5.exists()检查是否有数据,可以避免数据放入cache里。 teacher.exists() 检测有效数据但不会放入缓存
6.iteraor() 方法虽然可以预防缓存不足的问题,但同样的也有弊端:遍历同一个QuerySet时,会重复执行
对象、单表、多表条件关联查询
# #----------双下划线[ __ ] 多表 正向 查询--------------- sch = School.objects.filter(name='若智小学').values('id') # 返回name='若智小学' 的id print(sch) # 结果:<QuerySet [{'id': 1}]> # 正向一对多查找: sch = School.objects.filter(name='若智小学').values('Grade_Class__name') print(sch) # 结果:<QuerySet [{'Grade_Class__name': '四年级二班'}]> # 正向多对多查找: grade = Grade.objects.filter(name='三年级一班').values('teacher__name') print(grade) # <QuerySet [{'teacher__name': None}, {'teacher__name': '王哲学'}]>
grade = Grade.objects.filter(teacher__name='王哲学').values('name') print(grade) # <QuerySet [{'name': '三年级一班'}]> # #----------双下划线[ __ ] 多表 反向 查询--------------- # 反向一对多查找: grade = Grade.objects.filter(school__name='若智小学').values('name') print(grade) # <QuerySet [{'name': '四年级二班'}]> # 反向多对多查询 teacher = Teacher.objects.filter(grade__name='王哲学').values('name') # 取王哲学关联的班级 print(teacher) # <QuerySet [{'name': '四年级二班'}]>
# 一对多或者多对多查找都是通过 字段 来查找的
# 正向: 关联字段定义在谁的一方,就以谁为主
# 反向: 关联字段定义在其他表里,
聚合查询
from django.db.models import Avg, Max, Min, Sum # 所有班级的平均人数 grade = Grade.objects.all().aggregate(Avg('number')) print(grade) # 所有班级的总人数 grade = Grade.objects.all().aggregate(Sum('number')) print(grade) # 最大人数 grade = Grade.objects.all().aggregate(Max('number')) print(grade) # 最小人数 grade = Grade.objects.all().aggregate(Min('number')) print(grade)
Q查询和F查询
from django.db.models import F, Q q1 = Teacher.objects.filter(Q(name__startswith='李')).all() # 查询姓李的老师 print(q1) # 可以使用 | 和 & q1 = Teacher.objects.filter( Q(name__startswith='李')|Q(name__startswith='张')).all() print(q1) # ~ 否定 q1 = Teacher.objects.filter( ~Q(name__startswith='李')).all() # 查询不是姓李的老师 print(q1)
---------------F查询-----------------
from django.db.models import F, Q
# 一个班级,突然多了一个插班生
Grade.objects.filter(name='四年级二班').update(number=F('number')+1)
二:admin配置
admin功能很强大,它能从数据库读取数据并呈现在页面里,并且可以在前端页面实现对数据库的操作
注册model模型到admin:
1:register方法 admin.site.register(模型类, 定制类) 2:register 装饰圈注册 @admin.register(模型类)
定制管理类:
list_display: 指定要显示的字段 search_fields: 指定搜索的字段 list_filter: 指定列表过滤器 ordering: 指定排序字段
from django.contrib import admin from app01.models import * # Register your models here. class MyAdmin(admin.ModelAdmin): list_display = ('id', 'name', 'site',) search_fields = ('name',) list_filter = ('name',) ordering = ('id',) fieldsets = [(None, {'fields':['site']})] admin.site.register(School, MyAdmin) admin.site.register(Teacher) admin.site.register(Grade)
---恢复内容结束---