Django模型
-
模型,就是
python
中的类对应数据库中的表 -
ORM
:对象关系映射
示例
- 模型类必须继承
models.Model
- 每个属性对应数据库中的一个字段
- 表名自动使用
mysite_类名
的小写(如:polls_question
),可以覆盖 - 如果模型类中没有指定
primary_key
,那么会自动创建一个id
字段,自增
,主键
模型:
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField()
对应mysql数据库中的表:
CREATE TABLE `polls_question` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`question_text` varchar(200) NOT NULL,
`pub_date` datetime(6) NOT NULL,
PRIMARY KEY (`id`)
)
应用模型
当编写好了模型之后,需要将模型应用到数据库中:
1、配置应用
- 将模型对应的应用程序添加到项目的
settings
中:
INSTALLED_APPS = [
'polls'
]
- 在
settings
中配置正确的数据库连接:
"""
sqlite3数据库连接方式:
"""
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
"""
mysql数据库连接方式:
"""
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'model_study',
'USER': 'root',
'PASSWORD': '123456',
'HOST': '127.0.0.1',
'PORT': '3306',
}
}
-
安装对应数据库的驱动
-
如果是
mysql
,django2.2
需要安装mysqlclient
库 -
如果是
sqlite3
,是不需要额外安装,python
自带驱动库 -
一些数据库需要首先创建数据库,譬如:
mysql
、oracle
、mssql
等
-
2、预备迁移
在项目根目录的cmd
中运行:
python manage.py makemigrations polls
polls
是子应用的名称,如果不指定,那么就是对所有INSTALLED_APPS
中的应用都进行预备迁移- 指定该命令后,在对应的子应用下的
migrations
中会生成一个对应的迁移文件
3、正式迁移
在根目录的cmd
中运行:
python manage.py migrate
- 没有添加子应用名,那么就会把
django
项目中所有的应用都迁移到数据库中
4、模型修改后重新应用
- 不管是
新增模型
,还是修改
已有模型后,只需要重复执行第2步和第3步,即可自动实现数据库中的表结构、表关系等信息的修改
5、逆向从数据库表生成模型类
-
settings
中设置好DATABASES
配置 -
在对一个数据库中
建立
好表、约束
和表关系
等 -
在根目录的cmd中运行:
python manage.py inspectdb > polls/models.py
- polls是子应用名
-
第3步执行后会在
models
中生成对应的模型类class DjangoSession(models.Model): session_key = models.CharField(primary_key=True, max_length=40) session_data = models.TextField() expire_date = models.DateTimeField() class Meta: # 这个属性是通知django,不需要进行从模型到数据库的迁移管理 managed = False # 对应的数据库中的表名 db_table = 'django_session'
字段Field
模型类的属性对应数据库中表的字段,都是对应的Field类的实例
1、字段命名限制
-
字母,数字,下划线,首字母不能是数字
-
字段名称不能是Python保留字
-
由于
Django
查询查找语法的工作方式,字段名称不能在一行中包含多个下划线
2、AutoField、ID、PRIMARY_KEY
-
默认会自动创建一个自增,主键的id列
-
如果指定了
primary_key
为其它列,那么不会自动创建id
列 -
可以在模型中指定:
id = models.AutoField(primary_key=True)
3、常见Field
AutoField
BooleanField
CharField
DateField
DateTimeField
FloatField
SmallIntegerField
IntegerField
TextField
UUIDField 使用:
import uuid
from django.db import models
class MyUUIDModel(models.Model):
# uuid.uuid4 千万别写成 uuid.uuid4() ,不要写 ()
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
4、Field常见参数
max_length
:字段最大长度,用于字符串等,字符串类型CharField
必须设置该值null
:如果True
,Django
将在数据库中存储NULL
空值。默认是False
blank
:如果``True,该字段被允许为空白" "
。默认是False
。请注意,这不同于null
。null
纯粹是与数据库相关的,而blank
与验证相关。如果一个字段有blank=True
,表单验证将允许输入一个空值。如果一个字段有blank=False
,该字段将是必需的。choices
:示例:YEAR_IN_SCHOOL_CHOICES = (('FR', 'Freshman'),('SO', 'Sophomore'),('JR', 'Junior'),('SR', 'Senior'),('GR', 'Graduate'))
,中文示例:SEX_CHOICES=((1, '男'),(2, '女'))
,元组中的第一个元素是将存储在数据库
中的值,第二个元素是将在页面中显示
的值,最常见用于下拉选择框select
。default
:字段的默认值help_text
:用于显示额外的“帮助”文本primary_key
:如果True
,这个字段是模型的主键,默认是False
unique
:如果True
,该字段在整个表格中必须是唯一的verbose_name
:详细字段名,不指定则是属性名的小写,并且用 空格 替换 ‘_’
5、模型之间的关系
-
主外关系中,关联操作最常用的是:
models.CASCADE
级联删除 和models.SET_NULL
设置为null
-
一对多关系中,
ForeignKey
写在一对多关系中,多的那个模型中
1、一对多
使用django.db.models.ForeignKey
例如,如果一个Car模型有一个Manufacturer, 也就是说,一个 Manufacturer可以对应多个汽车,但Car只有一个汽车生产商Manufacturer,那么使用以下定义:
from django.db import models
class Manufacturer(models.Model):
name = models.CharField(max_length=20)
class Car(models.Model):
# 外键名是 对应类名的小写
# on_delete 是必须属性
manufacturer = models.ForeignKey(Manufacturer, on_delete=models.CASCADE)
name = models.CharField(max_length=20)
2、一对一
使用django.db.models.OneToOneField
例如:地址Place和餐馆Restaurant是一对一的关系,而餐馆Restaurant和服务员Waiter是一对多的关系
from django.db import models
class Place(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=80)
def __str__(self):
return "%s the place" % self.name
class Restaurant(models.Model):
place = models.OneToOneField(
Place,
on_delete=models.CASCADE,
primary_key=True,
)
# BooleanField 在数据库使用 tinyint 类型
serves_hot_dogs = models.BooleanField(default=False)
serves_pizza = models.BooleanField(default=False)
def __str__(self):
return "%s the restaurant" % self.place.name
class Waiter(models.Model):
restaurant = models.ForeignKey(Restaurant, on_delete=models.CASCADE)
name = models.CharField(max_length=50)
def __str__(self):
return "%s the waiter at %s" % (self.name, self.restaurant)
3、多对多
1. 自关联:
from django.db import models
class Student(models.Model):
name = models.CharField(max_length=20)
friends = models.ManyToManyField("self")
-
会生成一个中间表,如下
CREATE TABLE `test_app_student_friends` ( `id` int(11) NOT NULL AUTO_INCREMENT, `from_student_id` int(11) NOT NULL, `to_student_id` int(11) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `test_app_student_friends_from_student_id_to_stude_7ef9880e_uniq` (`from_student_id`,`to_student_id`), KEY `test_app_student_fri_to_student_id_154a4deb_fk_test_app_` (`to_student_id`), CONSTRAINT `test_app_student_fri_from_student_id_c400b5d4_fk_test_app_` FOREIGN KEY (`from_student_id`) REFERENCES `test_app_student` (`id`), CONSTRAINT `test_app_student_fri_to_student_id_154a4deb_fk_test_app_` FOREIGN KEY (`to_student_id`) REFERENCES `test_app_student` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
2. 简易多对多
class SchoolClass(models.Model):
name = models.CharField(max_length=20)
class Teacher(models.Model):
name = models.CharField(max_length=10)
school_class = models.ManyToManyField(SchoolClass)
-
会自动生成一个中间表,DDL语句如下:
CREATE TABLE `test_app_teacher_school_class` ( `id` int(11) NOT NULL AUTO_INCREMENT, `teacher_id` int(11) NOT NULL, `schoolclass_id` int(11) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `test_app_teacher_school__teacher_id_schoolclass_i_f52c7361_uniq` (`teacher_id`,`schoolclass_id`), KEY `test_app_teacher_sch_schoolclass_id_7ac34d1e_fk_test_app_` (`schoolclass_id`), CONSTRAINT `test_app_teacher_sch_schoolclass_id_7ac34d1e_fk_test_app_` FOREIGN KEY (`schoolclass_id`) REFERENCES `test_app_schoolclass` (`id`), CONSTRAINT `test_app_teacher_sch_teacher_id_8c52afbd_fk_test_app_` FOREIGN KEY (`teacher_id`) REFERENCES `test_app_teacher` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
其中:
test_app_teacher_school_class
是表名:test_app
是应用名,teacher
是第一个模型名,school_class
是第二个模型名
3. 自定义中间表
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(
Person,
through='Membership', # 必须是 类名的字符串 ,用 '' 包裹
through_fields=('group', 'person'),
)
class Membership(models.Model):
group = models.ForeignKey(Group, on_delete=models.CASCADE)
person = models.ForeignKey(Person, on_delete=models.CASCADE)
level = models.IntegerField(default=1)
- 通过
through='Membership'
指定Membership
作为中间表 - 通过
through_fields=('group', 'person')
指定中间模型的属性 - 一般需要自定义中间表时,都是有额外的字段,譬如
level = models.IntegerField(default=1)
方法
- 除了运行程序时,可以测试模型,还可以在根目录的
cmd
执行:
python manage.py shell
- 打开
django
脚本控制台,测试执行模型的方法,会比启动项目更方便 - 首先需要导入:
from Model_app.models import *
- 模型对象中有一个
objects
属性,该属性是管理器Manager
类型的对象,几乎所有的方法都是通过该对象执行。
1、新增
save
或 create
p = Person.objects.create(first_name="Bruce", last_name="Springsteen")
或
p1 = Person(first_name="Bruce", last_name="Springsteen")
p1.save()
2、修改
save
p = Person(first_name="Bruce", last_name="Springsteen")
p.first_name = 'James'
"""
force_insert=True 强制刷新
force_update=True 强制新增
不能同时使用2个参数,几乎用不上这2个参数
"""
p.save(force_insert=True)
obj = MyModel.objects.create(val=1)
# 需要使用F来保证不会出现并发冲突
MyModel.objects.filter(pk=obj.pk).update(age=F('age') + 1)
# 更新多个值
Entry.objects.filter(pub_date__year=2007).update(headline='Everything is the same')
3、查询
- 大部分检索是懒惰执行的,只在真实获取值的时候,才会去连接数据库获取数据
- 查询集通过过滤器进行查询,允许链式调用
pk
是主键的别名primary key
,如果真实主键是id
,那么pk
和id
使用是一样的效果
1. 过滤器
"""
# get 获取一个对象
# 查询主键等于 1 的 , 如果主键是ID,也可以使用 id=1
# 如果条件找不到对应的记录,会抛出 DoesNotExist 错误
# 如果条件找到多个记录,会抛出 MultipleObjectsReturned 错误
"""
person = Person.objects.get(pk=1)
"""
# all 获取所有对象
# 查询所有,得到的QuerySets对象
"""
all_persons = Person.objects.all()
"""
# exclude 例外
# 查询 日期年份 不是2006的
"""
persons = Person.objects.exclude(pub_date__year=2006)
"""
# filter 获取对象列表
# 查询 日期年份 是 2006 的
"""
persons = Person.objects.filter(pub_date__year=2006)
"""
# filter 获取对象列表,支持切片,但是不支持负数切片
# limit 5 :前5个
"""
persons = Person.objects.filter(pub_date__year=2006)[:5]
# limit 5,5 : 第6个到10个
persons = Person.objects.filter(pub_date__year=2006)[5:10]
# Entry.objects.all()[-1] 不支持
"""
# 返回前10个记录的, 0 ,2 , 4, 6, 8, 10 ,
并且会立刻执行,而不是懒惰执行
"""
Entry.objects.all()[:10:2]
"""
# 可以在结果集上,继续检索,允许链式调用
# 结果集是一个QuerySet
"""
q1 = Entry.objects.filter(headline__startswith="What")
q2 = q1.exclude(pub_date__gte=datetime.date.today())
q3 = q1.filter(pub_date__gte=datetime.date.today())
# 等同于:
q3 = Entry.objects.filter(headline__startswith="What").exclude(pub_date__gte=datetime.date.today()).filter(pub_date__gte=datetime.date.today())
"""
# order_by() 对结果集排序
"""
person_li = Person.objects.filter(pub_date__year=2006).order_by('pub_date')
# 支持多个字段, 类似 SQL: order by pub_date, headline
person_li = Person.objects.filter(pub_date__year=2006).order_by('pub_date', 'headline')
"""
# count() 对结果集统计
"""
count = Person.objects.filter(pub_date__year=2006).count()
"""
# first() 返回结果集的第一个对象
"""
person = Person.objects.filter(pub_date__year=2006).first()
person = Person.objects.first()
"""
# last() 返回结果集的最后一个对象
"""
person = Person.objects.filter(pub_date__year=2006).last()
"""
# values() 返回一个 字典对象 列表
"""
person_dict_li = Person.objects.filter(pub_date__year=2006).values()
- 大部分检索是懒惰的,只在真实获取值的时候,才会真正去连接数据库获取数据:
# 懒惰执行
q = Entry.objects.filter(headline__startswith="What")
q = q.filter(pub_date__lte=datetime.date.today())
q = q.exclude(body_text__icontains="food")
print(q) # 此处才会真的去连接数据库获取记录
# 返回前10个记录的, 0 ,2 , 4, 6, 8, 10 ,并且会马上执行,而不是懒惰执行
q = Entry.objects.all()[:10:2] # 已经获取到数据了
print(q)
2. 字段查找
-
字段检索,是在字段名后加
__
双下划线,再加关键字,类似 SQL 语句中的 where 后面的部分, 如:字段名__关键字
-
在查找中指定的字段必须是模型字段的名称
-
如果是
ForeignKey
字段,则是属性名+ _id:Entry.objects.filter(blog_id=4)
, 定义的ForeignKey
是blog
-
常见的字段检索:
exact
:判断是否等于value,一般不使用,而直接使用 ‘=’contains
:是否包含,大小写敏感,如果需要不敏感的话,使用icontains
startswith
:以value开头,大小写敏感endwith
:以value结尾,大小写敏感in
:是否包含在范围内isnull
:是否为null, 如:filter(name__isnull=Flase)
gt
:大于,如:filter(sage__gt=30) , 年龄大于30gte
:大于等于lt
:小于lte
:小于等于year/month/day/week_day/hour/minute/second
:时间查询
如:filter(pub_date\_\_year=2015)
年份是2015的,filter(pub_date__day=15)
天数是15的# 确定的搜索 ,SQL: where id = 14 Blog.objects.get(id__exact=14) # 等同于 Blog.objects.get(id=14) # 不区分大小写的确定搜索,匹配 beatles blog 、Beatles blog等 Blog.objects.get(name__iexact="beatles blog") # 包含,contains ,SQL:WHERE headline LIKE '%Lennon%' Entry.objects.get(headline__contains='Lennon') # 不区分大小写的包含 Entry.objects.get(headline__icontains='Lennon') # 以什么开头, SQL: WHERE headline LIKE 'Lennon%' # 还有 不区分大小写的 istartwith Entry.objects.get(headline__startswith='Lennon') # 同样有 endswith ,SQL : WHERE headline LIKE '%Lennon' # 还有 不区分大小写的 iendswith Entry.objects.get(headline__endswith='Lennon') # in Entry.objects.get(headline__in=['Lennon', 'Terry'])
3. 多对象关联
- 通过
Blog
模型中,关联的另一个模型对象entry
的属性进行过滤: entry__headline__contains
,即使是访问模型对象entry
的属性headline
,也必须使用__
# 检索所有Blog具有至少一个对象Entry ,其headline包含'Lennon'
Blog.objects.filter(entry__headline__contains='Lennon')
# Blog中 有一个对象 entry 的 headline 中包含“Lennon”并且 是 2008年发布的
Blog.objects.filter(entry__headline__contains='Lennon').filter(entry__pub_date__year=2008)
# 通过 , 分割多个条件, 相当于数据库中的 'and'
Blog.objects.filter(entry__headline__contains='Lennon', entry__pub_date__year=2008)
# 取上面相反的值
Blog.objects.exclude(entry__in=Entry.objects.filter(headline__contains='Lennon', pub_date__year=2008, ))
- 一对多关系中,通过一个模型获取另一个模型:
from django.db import models
class Manufacturer(models.Model):
name = models.CharField(max_length=20)
class Car(models.Model):
manufacturer = models.ForeignKey(Manufacturer, on_delete=models.CASCADE)
name = models.CharField(max_length=20)
# 从 一的模型 查找 多的模型
# 通过 '多的模型小写名_set' 查找
manufacturer = Manufacturer.objects.first()
cars = manufacturer.car_set.all()
# 从 多的模型 查找 一的模型
car = Car.objects.first()
manufacturer = car.manufacturer
- 多对多关系中,通过一个模型获取另一个模型:
class SchoolClass(models.Model):
name = models.CharField(max_length=20)
class Teacher(models.Model):
name = models.CharField(max_length=10)
school_class = models.ManyToManyField(SchoolClass)
"""
从 没有写 ManyToManyField 的模型查找另一 写了 ManyToManyField 的模型
需要在 查询的模型名的小写后 加 _set
"""
schoolClass = SchoolClass.objects.first()
teachers = schoolClass.teacher_set.all()
"""
从 写了 ManyToManyField 的模型查找另一个模型
"""
teacher = Teacher.objects.first()
schoolClasses = teacher.school_class.all()
4. 聚合函数
-
使用
aggregate()
函数返回聚合函数的值 -
常用的聚合函数有:
Avg
、Count
、Max
、Min
、Sum
# Max 找出最大的 from dango.db.models import Max Person.objects.aggregate(Max('age')) # 结果是一个字典 {'age__max': 30} # 可以使用 max=Max('age') 指定 别名为 max,而不使用 age__max Person.objects.aggregate(max=Max('age')) # 多个聚合函数一起使用 Person.objects.aggregate(Max('age'), Min('age'), Avg('age'))
5. 分组查询
使用annotate()函数实现分组查询,得配合其他函数:
annotate
:用于分组,配合Avg
,Count
等聚合函数,如:annotate(max=Max('age'))
filter
: 用于过滤,在annotate
之前使用表示where
条件,在annotate
之后使用表示having
条件values
:在annotate
之前使用表示分组
字段,在annotate
之后使用表示取值
"""
基本应用
以 group_id 分组,找出level的最大值,最小值,和平均值
"""
Membership.objects.values('group_id').annotate(max=Max('level'), min=Min('level'), avg=Avg('level'))
"""
以 group_id 分组 并且 group_id 大于 2 ,找出level的最大值,最小值,和平均值
"""
Membership.objects.filter(group_id__gt=2).values('group_id').annotate(max=Max('level'), min=Min('level'), avg=Avg('level'))
4、删除
- delete方法
person = Person.objects.get(pk=1)
person.delete()
5、刷新对象
- 通过 refresh_from_db 从数据库中重新获取对象的内容
person = Person.objects.get(pk=1)
person.refresh_from_db()
6、Q对象
filter()
等方法中的关键字参数查询都是并且(‘AND’)的, 如果你需要执行更复杂的查询(例如or语句),那么可以使用Q 对象
。Q 对象
(django.db.models.Q
) 对象用于封装一组关键字参数,可以使用&
和|
操作符组合起来,当一个操作符在两个Q 对象
上使用时,它产生一个新的Q 对象
。
from django.db.models import Q
# 等同于 select * from poll where question like 'Who%' or question like 'What%'
poll = Poll.objects.filter(Q(question__startswith='Who') | Q(question__startswith='What'))
# 等同于 select * from poll WHERE question like 'Who%' and (pub_date = '2005-05-02' or pub_date = '2005-05-06')
poll = Poll.objects.filter(
Q(question__startswith='Who'),
Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6))
)
# Q对象可以使用 ~ 操作符取反, 相当于SQL中 not
poll = Poll.objects.filter(
Q(question__startswith='Who'),
~Q(pub_date__year=2005)
)
# Q对象可以和一般的关键字参数混用, 但是Q对象必须在一般关键字参数的前面
Poll.objects.filter(
Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)), question__startswith='Who')
7、F对象
模型的属性名出现在操作符的右边,就使用F对象进行包裹
- 可以使用模型的A属性与B属性进行比较
"""
找出女生数量大于男生数量的年级
对应sql:select * from grade where girlnum > boynum
"""
grades = Grade.objects.filter(girlnum__gt=F('boynum'))
- 支持算数运算
"""
找出女生数量大于 男生数量+10 的年级
对应的sql: select * from grade where girlnum > boynum + 10
"""
Grade.objects.filter(girlnum__gt=F('boynum') + 10)
"""
所有书籍的价格 +1
对应的 sql: update book set price = price + 1
"""
Book.objects.update(price=F("price")+1)
使用SQL语句
1、通过模型使用SQL
- 通过
raw
函数执行原始SQL
语句进行查询 - 主键字段必须包含在查询的字段中,不然会引发错误
# 定义个 person 模型
class Person(models.Model):
first_name = models.CharField()
last_name = models.CharField()
birth_date = models.DateField()
"""
# 执行 原始 SQL
# 表名前面必须加 应用名myapp,即在数据库中的真实表名,否则会抛出异常
"""
for p in Person.objects.raw('SELECT * FROM myapp_person'):
print(p)
# 字段先后顺序没关系
Person.objects.raw('SELECT id, first_name, last_name, birth_date FROM myapp_person')
# 等同于
Person.objects.raw('SELECT last_name, birth_date, first_name, id FROM myapp_person')
"""
# 可以从其他表格中查询出匹配 person 模型的记录集
# 总之最终的数据集的结构必须和 Person一样
"""
Person.objects.raw('SELECT first AS first_name, last AS last_name,bd AS birth_date,pk AS id,FROM some_other_table')
# 返回的结果集一样可以执行切片
first_person = Person.objects.raw('SELECT * FROM myapp_person')[0]
# 但是上述语句会返回所有结果,基于节省传输的需要,在数据库缩小结果集范围更正确
first_person = Person.objects.raw('SELECT * FROM myapp_person LIMIT 1')[0]
# 传递参数
lname = 'Doe'
Person.objects.raw('SELECT * FROM myapp_person WHERE last_name = %s', [lname])
2、避开模型使用SQL
- 不应用模型,直接使用
SQL语句
进行增删改查
from django.db import connection
def my_custom_sql(obj):
with connection.cursor() as cursor:
cursor.execute("UPDATE bar SET foo = 1 WHERE baz = %s", [obj.baz])
cursor.execute("SELECT foo FROM bar WHERE baz = %s", [obj.baz])
row = cursor.fetchone()
return row
来源:CSDN
作者:Python'sGod
链接:https://blog.csdn.net/weixin_44733660/article/details/104117732