模型用来定义Django中数据的结构和行为,通常情况下每个Django模型映射数据库中的一张表。
Django模型—>数据库表
1、定义与使用模型
定义模型类
实例:定义books应用书籍信息模型类
模型类定义在应用的models.py文件中,并继承models.Model类
from django.db import models
class BookInfo(models.Model): #魔法方法类模型 BookInfo
name = models.CharField(max_length=20, verbose_name="名称") #五个字段
pub_date = models.DateField(verbose_name="发布日期")
readcount = models.IntegerField(default=0, verbose_name="阅读量")
commentcount = models.IntegerField(default=0, verbose_name="评论量")
is_delete = models.BooleanField(default=False, verbose_name="逻辑删除")
def __str__(self): #将查询结果以字符串形式显示
return self.name
注册应用
若要将模型映射到数据库,需要先在settings.py文件中将包含该模型的应用安装到INSTALLED_APPS中,即在该项中添加一个表示应用名的元素。
INSTALLED_APPS = [
...
'books',
]
映射
注册成功后,需要对模型中定义的模型类进行映射。映射分为两步:生成迁移文件和执行迁移文件。
-
生成迁移文件
就是通过ORM框架生成执行数据库操作所需的SQL语句。
python manage.py makemigrations #生成迁移文件
若出现如图信息,则表名成功生成迁移文件。在books应用的migrations目录中:
文件内容如下:
from django.db import migrations, models class Migration(migrations.Migration): initial = True dependencies = [ ] operations = [ migrations.CreateModel( name='BookInfo', #表示当前迁移数据的模型类名 fields=[ #该列表中包含定义模型类中的字段 ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('name', models.CharField(max_length=20, verbose_name='名称')), ('pub_date', models.DateField(verbose_name='发布日期')), ('readcount', models.IntegerField(default=0, verbose_name='阅读量')), ('commentcount', models.IntegerField(default=0, verbose_name='评论量')), ('is_delete', models.BooleanField(default=False, verbose_name='逻辑删除')), ], ), ]
若在定义模型类时没有指定主键,那么Django会自动创建id字段为主键。
-
执行迁移文件
就是执行迁移文件中的SQL语句。
先创建一个数据库
注:数据库名必须与配置文件settings.py中保持一致。
环境搭建好之后运行下面的命令,结果如图:
python manage.py migrate
遇到的坑:
ImportError: No module named ‘MySQLdb’ 参考文章
然后在数据库中查看:
所示的数据库表中出来books_bookinfo外,其他表都由Django内置的模型类映射而来。
至此,模型类与数据库表映射完毕。
2、模型的字段
每个模型类的字段对应数据表中的一个字段。模型字段是模型类的属性,它自身也是一个类。
模型类字段---->数据表字段
字段类型
也就是字段的数据类型。
AutoField #用于定义可自增的整型字段
BooleanField #用于定义布尔类型的字段(Ture或False)
CharField #用于定义字符串类型的字段
DateField #定义YYYY-mm-dd格式的日期字段
TimeField #定义HH:MM[:ss[.uuuuuu]]格式的时间字段
DateTimeField #定义日期时间字段
EmailField #定义邮箱字段
FileField #用于上传文件字段
ImageField #用于上传图片文件
IntegerField #用于定义整型字段
TextField #用于定义大文本字段
关系字段
就是定义表之间的关系:一对多、一对一、多对多。
ForeignKey #定义一对多
实例:以国家和人为例,一个国家包含很多人,国家和人之间具有一对多关系。
from django.db import models
class Country(models.Model):
country_code = models.CharField(max_length=20)
country_name = models.CharField(max_length=20)
class Meta:
db_table="country"
class Person(models.Model):
person_name = models.CharField(max_length=20)
person_age = models.IntegerField(default=0)
person_money = models.IntegerField(default=0)
person_nation = models.ForeignKey(Country, on_delete=models.CASCADE) #将person_nation设置为外键,与country表关联
class Meta:
db_table="person"
OneToOneField #定义一对一,它继承了ForeignKey
实例:以国家和总统为例,一个国家只能有一个总统,一个总统也只能属于一个国家,所以是一对一关系。
class President(models.Model)
president_name = models.CharField(max_length=20)
president_gender = models.CharField(max_length=10)
president_nation = models.OneToOneField(Country) #将president_nation字段设置为与Country相关联
class Meta:
db_table="president"
ManyToManyField #定义多对多
实例:教师与学生为多对多关系
class Teachers(models.Model):
name = models.CharField(max_length=10)
class Meta:
db_table="teachers"
class Students(models.Model):
name = models.CharField(max_length=10)
classes = models.ManyToManyField(Teachers) #将classes字段设置为与Teachers相关联
class Meta:
db_table="students"
3、模型的元属性
在模型类中添加内部类Meta的方式可以定义模型的元属性(如排序字段、数据表明、字段单复数等)
例如:
class BookInfo(models.Model):
...
class Meta:
db_table = 'tb_bookinfo'
常用元属性:
abstract # abstract=Ture则表示模型是抽象类。用来定义多个模型的共有信息,该模型不能被实例化,只能作为其他模型的基类
app_label # 指定当前模型所属的应用
ordering # 用于设置模型字段的排序方式
ordering = 'id' #以id字段为升序排序
ordering = ['-id'] #以id字段为降序排序
ordering = ['id','score'] #如果只根据id无法为记录排序,再根据score进行升序排序
verbose_name # 用于设置显示在后台管理系统页面上的、直观可读的数据表名
verbose_name = "book"
verbose_name = "图书"
verbose_name_plural # 设置模型类在后台管理系统页面上显示的表名的复数形式
verbose_name_plural = "books"
若没指定则默认在verbose_name加上’s’作为复数形式,例如verbose_name值为book,那么模型类名的复数形式为books.
4、Manager管理器
管理器是Django模型进行数据查询操作的接口,每个模型都拥有至少一个管理器。
管理器名称
objects为每个模型类的默认管理器,但是可以改名,示例:
from django.db import models
class Person(models.Model):
...
custom_objects = models.Manager() # 重命名为custom_objects
使用Person.custom_objects.all()会返回一个包含所有Person对象的列表。
自定义管理器
在models.py文件中实例化自定义的Manager管理器,就可以在定义的模型中使用自定义的Manager管理器。自定义管理器通常有两种方式:一是添加额外的管理器方法,二是修改Manager的原始查询集。
1、添加额外的自定义管理器方法
就是为模型类增加“表级”功能。
class CountryManager(models.Manager): #自定义管理器,其实就是一个类
def conuntry_name_prefix(self):
#查询所有结果
all_countries = self.all()
for country in all_countries:
country.country_name = '国家:' + country.country_name
return all_countries
class Country(models.Model):
country_code = models.CharField(max_length=20)
country_name = models.CharField(max_length=50)
objects = CountryManager() #指定管理器为CountryManager()
class Meta:
db_table = 'country'
def __str__(self):
return self.country_name
可以通过Country.objects.conuntry_name_prefix()查询所有国家名称。
5、数据的增删改查
添加数据
两种方法:
-
create()方法
格式:
create(self, **kwargs)
实例:模型类BookInfo通过调用create()方法添加数据
BookInfo.objects.create(name="骆驼祥子", readcount=100, pub_date="1937-1-2", is_delete=0, commentcount=70)
-
save()方法
格式:
save(force_insert=False, force_update=False, using=None, update_fileds=None)
实例:创建模型类BookInfo的实例,调用save()方法保存实例数据,示例如下:
bookinfo = BookInfo(name='围城', pub_date='1994-02-05', readcount=134, commentcount=100, is_delete=0) bookinfo.save()
查询数据
对象管理器提供了四个方法:
-
all()方法
查询模型在数据库映射表中的所有记录,返回值是一个QuerySet对象(可迭代类似列表)
实例:查询模型类BookInfo所对应数据表中所有的记录,示例如下
all_info = BookInfo.objects.all()
-
filter()方法
根据查询条件查询数据表,返回满足条件的数据,返回值是一个QuerySet对象。
实例:筛选出id值大于3的记录,示例如下
binfo = BookInfo.objects.filter(id__gt=3)
-
exclude()方法
根据查询条件查询数据库,返回不满足条件的数据,返回值是一个QuerySet对象。
实例:获取id值小于3的记录,示例如下
ex_binfo = BookInfo.objects.exclude(id_gte=3)
-
get()方法
返回符合条件的一条记录,返回值是一个模型对象
实例:查询数据表books_bookinfo中id值为1的记录,示例如下
binfo = BookInfo.objects.get(id=1)
删除数据
delect()方法
立即删除数据库中的记录,并返回删除记录的数量
binfo.delete()
更新数据
update()是对象管理器的方法,用于根据查询条件更新数据表的指定字段,并返回生效的行数。
格式:update(self, **kwargs)
实例:将模型类BookInfo对应的数据表中id为1的is_delete值修改为1,实例如下
BookInfo.objects.filter(id=1).update(is_delete=1)
6、QuerySet的使用
QuerySet是Django的数据查询集,表示从数据库中获取的对象集合。
多表查询
- 正向查询:关联字段定义在当前表中,则从当前表查询关联表为正向查询
- 反向查询:关联字段不在当前表中,则从当前表查询关联表为反向查询
1、一对多关系
以国家和人民为例,关联字段定义在模型Person中,那么使用Person对象查询Country对象为正向查询,使用Country对象查询Person对象为反向查询。
-
正向查询
格式:当前模型对象.关联字段.关联模型中要查询的字段
实例:查询某人所属的国家,假设此人id为1
p = Person.objects.get(id=1) p_country = p.person_nation.country_name
person_nation为两模型的关联字段,country_name为country模型中的字段
-
反向查询
格式:当前模型对象.关联模型表名(小写)_set.all()
实例:查询某国包含人员,假设该国家id为1,示例如下:
c = Country.objects.get(id=1) p_country = c.person_set.all()
以Country模型查询Person模型中的数据,其中关联模型为Person.
一对一关系和多对多关系与上面类似。
F对象和Q对象
如果在查询中需要比较表中的字段,可以使用django.db.models中的F对象;若查询条件为多个,则用Q对象。
-
F对象
格式:F(字段名)
实例:查询BookInfo模型类中,阅读量(readcount)大于评论量(commentcount)的图书。
from django.db.models import F BookInfo.objects.filter(readcount__gt=F('commentcount')) #BookInfo.objects.filter(readcount=F('commentcount')*2) #支持简单运算
-
Q对象
格式:Q(属性名__运算符=值)
实例:查询阅读量大于20且编号小于3的图书
BookInfo.objects.filter(Q(readcount__gt=140)&Q(id__lte=5)) #Q对象支持取反操作~Q(id=3)表示id不等与3
QuerySet的特性
惰性执行和缓存
7、执行原始SQL语句
两种方法:1、使用模型管理器的raw()方法;2、利用Django提供的默认数据库django.db.connection获取游标对象,再通过游标对象调用execute()方法执行SQL语句。
使用Manager.raw()方法
格式:Manager.raw(raw_query, params=None, translations=None)
实例:查询数据表person中所有数据,示例如下:
person = Person.objects.raw("select * from person")
等价于Person.objects.all()
raw()方法将查询语句中的字段映射到模型字段,因此raw()方法中字段的顺序并不影响查询出的结果,如:
Person.objects.raw("select id,person_age,person_name from person")
Person.objects.raw("select person_age,id,person_name from person")
#二者查询结果一致
注:raw()方法的查询语句中必须包含主键
示例:
Person.objects.raw("select pk as id,p_age as person_age,p_name as person_name from person")
代码与数据分离:
-
translations方式
query_map = {"pk:id","p_age:person_age","p_name:person_name"} Person.objects.raw("select * from person", translations=query_map) #传字段 person = Person.objects.raw("select * from person")[0] #raw()方法支持索引
-
params方式
param = ['person_name'] p_name = Person.objects.raw("select id,%s from person", param) #传字段
param = {"id"=1} p_name = Person.objects.raw("select * from person where id=%(id)s", param) #传参
利用游标对象
from django.db import connection
conn = connection.cursor() #获取游标对象
conn.execute('select * from person') #直接执行语句
conn.fetchone() #查询一条语句
conn.fetchall() #查询所有记录