模型部分讲解
本文是自己学习做的笔记,如有雷同纯属巧合,如有问题请指出,谢谢!
基于环境Ubuntu16.04
python3.6
Django 2.07
[储备知识] ORM(对象object-关系relationship-映射map)
ORM的作用->完全面向对象不需要对任何sql语句进行操作,这样修改数据库(orcale/sqlite3/mysql)时只需要对少量代码进行修改
(1)数据库的更换
1-在mysql下创建数据库,如名为test2(Django默认是sqlite3的数据库,换库配置需要换)
2-更换mysql数据库: 在settings文件中的数据库做如下配置:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'test2',
'USER': 'root',
'PASSWORD': 'mysql',
'HOST': 'localhost',
'PORT': '3306'
}
}
这里的name为需要使用到的mysql数据库的名称,USER PASSWORD为登陆数据库的用户名密码
HOST为数据库的IP地址,PORT默认为3306
注意python3可能安装不上MYSQLdb可以用pymysql代替
方法为
import pymysql
pymysql.install_as_MySQLdb()
Django根据属性的类型确定:当前选择数据库支持字段的类型以及默认的控件类型,比如搜索框等
Django会为表自动添加primary key每个模型只有一个pk(primary key)如果已经赋予了一个PK则不会自动添加
在models里面定义表时,表名注意:1-不能设为python预留的名称 2-不能包含__两个以上连续的下划线(__在后面做查询的时候用到)
[一]表内属性的定义类型
1-字段类型
常用的字段类型有:
from django.db.models as models
models.BooleanField
models.FloatField
models.TimeField
models.DateTimeField
models.DecimalField(max_digits,decimal_places) # 第一个参数是总的数的位数,第二个数是小数的位数
models.FileField: 一个上传文件的字段 models.ImageField:保证上传是个有效的image
# 这二者一般而言不会用,一般只会把文件上传到云
# 然后上传磁盘上的地址就可以
………………等字段的类型,都以Field结尾
(在设计表的时候可以定义一个isDelete的属性做逻辑删除不做物理删除)
2-字段选项
null --->True Django将null存储到数据库
blank --->True允许该字段为空白
db_column --->字段的名称,如果未指定就用属性的名称
db_index --->如果为True则在表中为字段创建索引(加速查询)
default --->默认值
primary --->是否为主键 此时字段设置为AutoField。
unique --->如果为True这个字段在表中必须有唯一值
用法如下
hname = models.CharField(max_length=20, db_column='Hero Name')
hgender = models.BooleanField(default=True)
3-关系(都在models中)
ForeignKey: 一对多,将字段定义在多的端中
ManyToMany: 多对多,定义在连短中
OneToOneField: 一对一将字段定义在任意一端中
用一访问多: 对象.模型类小写_set book.heroinfo_set
用一对一访问: 对象.模型类小写 hero.bookinfo
访问id 对象.属性_id hero.book_id
Example:
class HeroInfo(models.Model):
hname = models.CharField(max_length=20, db_column='Hero Name')
hgender = models.BooleanField(default=True)
isDelete = models.BooleanField(default=False)
hcontent = models.CharField(max_length=100)
hbook = models.ForeignKey(BookInfo)
book = BookInfo()
这里就和之前的学习笔记一样,一对多的情况把外键放在多的一端
查询一本书里面的英雄 book.heroinfo_set
在这里把一对多 一认为是上端,而多那个端为下端
查询下端的方法 实例名.类属性名_set用来查询外键信息(在子关联的地方会讲到上端的查询方法)
查询英雄所在的书籍 对象.属性 hero.book
比如:肯德基里面有很多食品,做相互关联
class food():
"""定义了很多类属性"""
h_shop = models.ForeignKey(Shop)
[二]元选项
在模型类中定义类Meta用于设置元信息
class BookInfo(models.Model):
btitle = models.CharField(max_length=20, null=False)
bpub_date = models.DateTimeField(null=True, db_column='publication date')
bread = models.IntegerField(default=0)
bcomment = models.IntegerField(default=0)
isDelete = models.BooleanField(default=False)
class Meta:
db_table='bookinfo'
ordering=['id']
books1 = models.Manager()
books2 = BookInfoManaer()
db_table: 定义数据表名, 默认名称<app_name>_<model_name> 这个数据表的名字更改的是他在数据库中的名字
例如:原模型类名为BookInfo
这里将db_table = 'book1'
则在数据库中创建表的时候名为book1而不是bookinfo(这里都转换为了小写)
ordering: 对象的默认排序字段,获取对象的列表时使用(获取查询时候的顺序)
ordering=['id']按照id正向输出,ordering=[-'id']倒序输出
元选项是在表生成前定义的表的属性,例如排序以及表的名称……
[三]管理器的说明
模型定义完后有很多类方法
比如BookInfo.objects.all()等 objects 是继承的Manager类型对象,负责和数据库进行交互,一般不作修改
每个Django模型必须有至少一个管理器,如果自己重新定义,objects默认就没了
自定义管理器的主要作用:
1-向管理器中添加额外的方法(例如下面提到的create方法)
2-修改管理器返回的原始查询集:重写get_queryset()方法 修改原始的方法
class BookInfoManager(models.Manager):
def get_queryset(self):
# 修改了查询的方法
# 这个改写的意思是首先先继承父类的get_queryset方法,然后过滤出未被逻辑删除的显示
return super(BookInfoManager, self).get_queryset().filter(isDelete=False)
class BookInfo(models.Model):
……
books1 = BookInfoManager()
books2 = models.Manger()
# 如果自己定义了一个管理器那么一定要创建一个指向models.Manger(),因为自己创建了管理器,默认的
# object 就不会被生成
接下来总结管理器的第二个功能:
定义了上述的books1, books2可以在shell中
b = BookInfo(), b.btitle=xxx
BookInfo.books2.all() #进行查询 会显示所有数据
若用BookInfo.books1.all() 按照上面改写的Manager()只会现实未被逻辑删除的结果
但是这样的方法太繁琐,如果能够直接增加删除数据就更加方便了:
首先对property属性、类方法和静态方法进行复习:
特性property:property可以保护似有属性,通过一段函数返回特定的类属性,保护了私有属性
import math
class Circle:
def __init__(self,radius): #圆的半径radius
self.__radius=radius
@property
def area(self):
return math.pi * self.radius**2 #计算面积
@property
def perimeter(self):
return 2*math.pi*self.radius #计算周长
c=Circle(10)
print(c.area) #可以向访问数据属性一样去访问area,会触发一个函数的执行,动态计算出一个值
print(c.perimeter) #同上
输出结果:
314.1592653589793
62.83185307179586
私有属性 radius不被外界访问
静态方法:一般用于类内使用,在继承的过程中不能继承,用于类内的动态赋值
class Date:
def __init__(self,year,month,day):
self.year=year
self.month=month
self.day=day
@staticmethod
def now(): #用Date.now()的形式去产生实例,该实例用的是当前时间
t=time.localtime() #获取结构化的时间格式
return Date(t.tm_year,t.tm_mon,t.tm_mday) #新建实例并且返回
@staticmethod
def tomorrow():#用Date.tomorrow()的形式去产生实例,该实例用的是明天的时间
t=time.localtime(time.time()+86400)
return Date(t.tm_year,t.tm_mon,t.tm_mday)
a=Date('1987',11,27) #自己定义时间
b=Date.now() #采用当前时间
c=Date.tomorrow() #采用明天的时间
print(a.year,a.month,a.day)
print(b.year,b.month,b.day)
print(c.year,c.month,c.day)
这里b c 通过调用内部的静态方法,来定义了各自类内部的self.year, self.month, self.day
类方法:在继承过程中能进行传递
import time
class Date:
def __init__(self,year,month,day):
self.year=year
self.month=month
self.day=day
# @staticmethod
# def now():
# t=time.localtime()
# return Date(t.tm_year,t.tm_mon,t.tm_mday)
@classmethod #改成类方法
def now(cls):
t=time.localtime()
return cls(t.tm_year,t.tm_mon,t.tm_mday) #哪个类来调用,即用哪个类cls来实例化
class EuroDate(Date):
def __str__(self):
return 'year:%s month:%s day:%s' %(self.year,self.month,self.day)
e=EuroDate.now() # 类方法的调用是可以不是实例对象,是面向类的
print(e) #我们的意图是想触发EuroDate.__str__,此时e就是由EuroDate产生的,所以会如我们所愿
输出结果:
year:2017 month:3 day:3
若不用类方法,仅用静态方法输出结果如下
输出结果:
<__main__.Date object at 0x1013f9d68>
静态方法一般用于类内部
类属性用于类之间,因为可继承
对表进行操作时,若要快速添加一条信息
用b=BookInfo(),b.btitle=xxx这样程序化的输入可以封装成函数
__init__已经在models里面写了,通过改写init方法很麻烦
方法一:通过类方法进行创建
进入shell模式后
通过b = BookInfo.create(xx, datetime(xx, xx, xx))进行类方法的创建
class BookInfo(models.Model):
btitle = models.CharField(max_length=20, null=False)
bpub_date = models.DateTimeField(null=True, db_column='publication date')
bread = models.IntegerField(default=0)
bcomment = models.IntegerField(default=0)
isDelete = models.BooleanField(default=False)
books1 = models.Manager()
books2 = BookInfoManaer()
@classmethod
def create(cls, btitle, bpub_date):
b = BookInfo()
b.btitle = btitle
b.bpub_date = bpub_date
b.bread = 0
b.bcomment = 0
b.isDelete = False
b.save()
方法二:通过管理器manager实现模型类的快速创建:
class BookInfoManaer(models.Manager):
def get_queryset(self):
return super(BookInfoManaer, self).get_queryset().filter(isDelete=False)
def create(self, btitle, bpub_date):
b = BookInfo()
b.btitle = btitle
b.bpub_date = bpub_date
b.bread = 0
b.bcomment = 0
b.isDelete = False
b.save()
class BookInfo(models.Model): # 模型类
btitle = models.CharField(max_length=20, null=False)
bpub_date = models.DateTimeField(null=True, db_column='publication date')
bread = models.IntegerField(default=0)
bcomment = models.IntegerField(default=0)
isDelete = models.BooleanField(default=False)
class Meta:
db_table='bookinfo'
ordering=['id']
books1 = models.Manager() # 自定义管理器是定义一个类 该类是模型类的一个类属性
books2 = BookInfoManaer()
def __str__(self):
return self.btitle
然后可以通过
b = BookInfo.books2.create('abc', datetime(xx, xx, xx))
进行数据的增加
上面所属的元选项和管理器都是模型类的成员
[四]模型查询
1-查询集
惰性执行:创建查询集不会有数据库的访问,知道调用数据时才访问数据库
缓存: 每个查询集都包涵一个缓存来最小化对于数据库的访问
查询集=查询的得到数据组成的列表(生成器)
返回查询集的方法——过滤器:all(), filter(), exclude(), order_by(), values()
注意:
(1)values查询将一个对象(所有字段)构成一个字典,然后构成一个列表返回
(2)filter(键1=值1, 键2=值2) ======> filter(键1=值1).filter(键2=值2) 相当于逻辑与的关系
查询单个值的方法
(1)get():返回单个满足条件的对象 # 如果找到多条或为空都会报错
(2)count():返回当前查询的总条数
(3)first():返回第一个对象
(4)last():返回最后一个对象
(5)exisits():判断查询集中是否有数据
注意:
(1)对查询集支持用下标的方式进行索引,但是不支持负索引
(2)在缓存的时候只有用的数据是子集的时候不进行缓存
(3)
2-字段查询
实现where子列名 利用filter()/exclude()/get()参数
语法使用: [属性名称__比较运算符=值]
例如filter(title__contains='%') 过滤出 一定是等号
对于外键,使用属性名_id表示文件。HeroInfo.book_id即可查询heroInfo中某个英雄对应的书的id
比较运算符
1-exact:表示判等__exat可以不写默认判等
2-contains表示包涵(区分大小写)
3-startswith\endswith 以value开头或结尾的
例如exclude(btitle__endswith='d')
4-isnull\isnotnull
在上述运算符前面加一个i就不区分大小写查询
5-in表示查询在这个范围之内的信息
filter(pk__in=[1, 2, 3])
把主键值为1, 2, 3的都查找出来
gt/gte/lt/lte 大于/大于等于/小于/小于等于
filter(bpub_date__year=1980)
filter(bpub_date__gt=date(1980, 12, 31))
跨关联关系的查询:处理join查询
语法:模型类名<属性名><比较>
相当于inner join:
BookInfo.books1.filter(heroinfo__hname__contains="人民")
用法:外键类名(小写)__需要过滤的列名__比较运算符=值
查询书中英雄名中包含'人民'的书
关联查询的方式
3-聚合查询
聚合函数:Avg, Count, Max, Min, Sum
利用aggregate()返回聚合函数的值
聚合函数的用法
from django.db.models import Max
a = BookInfo.books.all()
a.aggregate(Max('btitle'))
a.count()
F对象,可以使模型的字段A与字段B进行比较,如果字段A写在了等号的左边,字段B写在了等号的右边需要通过
F对象使用在等号两边是两个字段类型的时候用
F对象进行构造
a = BookInfo.books1.filter(bread__gte=F('bcomment'))
这里bread和bcomment是一个模型类里面的两个不同字段
F对象还可以支持算数运算
a = BookInfo.books1.filter(bread__gte=F('bcomment')*2)
F对象还可以支持关联对象查询
a = BookInfo.books1.filter(isDelete=F('heroinfo__isDelete'))
Q对象
用来表达逻辑或 而不是通过filter(条件1, 条件2)做与关系
Q(条件1)|Q(条件2) 表达或对象
Q(条件1)&Q(条件2) 表达逻辑与 但是太麻烦不怎么用