一、创建model
django.db.models是django自带的创建数据库的ORM。
在models.py中以继承models.Model创建表后,需要在setttngs中确保添加了当前应用,并执行
python3 manage.py makemigrations app[应用名称]
python3 manage.py migrate app[应用名称]
来生成迁移文件并提交到数据库执行建表操作。
一、创建单张表
# models.py from django.db import models
class Person(models.Model): name = models.CharField(max_length=30) age = models.CharField(max_length=30)
from app02.models import Person p1 = Person(name="Jan", age=20) # 声明一个实例,也就是一条记录 p1.save() # 必须保存 p2 = Person() p2.name = "Li" p2.age = 18 # 可以声明一个实例,也可以修改一个字段,同样需要提交 p2.save()
二、创建一对一表
当一张既有的表需要扩充字段时,为了保持原有的表字段和结构不变,可以创建另一张表存储扩充的字段;第二张表的主键和第一张表的主键一一对应。
# models.py from django.db import models class Place(models.Model): name = models.CharField(max_length=50) address = models.CharField(max_length=80)
class Restaurant(models.Model): place = models.OneToOneField(Place,on_delete=models.CASCADE,primary_key=True) serves_hot_dogs = models.BooleanField(default=False) serves_pizza = models.BooleanField(default=False)
>>> p1 = Place(name='Demon Dogs', address='944 W. Fullerton') >>> p1.save() >>> p2 = Place(name='Ace Hardware', address='1013 N. Ashland') >>> p2.save()
>>> r1 = Restaurant(place=p1, serves_hot_dogs=True, serves_pizza=False) # 插入记录时,如果需要同时插入扩充的字段,就在后面继续添加扩充表 >>> r1.save()
>>> p1.restaurant
>>> r2.place
>>> hasattr(p2, 'restaurant') # 可以用hasattr检查是否已插入扩充字段
三、创建多对一表
# models.py from django.db import models class Reporter(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=30) email = models.EmailField() class Article(models.Model): headline = models.CharField(max_length=100) pub_date = models.DateField() reporter = models.ForeignKey(Reporter, on_delete=models.CASCADE)
>>> r1 = Reporter(first_name='John', last_name='Smith', email='john@example.com') >>> r1.save() # 一定要save,否则下面的Article外键无法绑定需要的reporter字段 >>> r2 = Reporter(first_name='Paul', last_name='Jones', email='paul@example.com') >>> r2.save() >>> from datetime import date >>> a1 = Article(id=None, headline="This is a test", pub_date=date(2005, 7, 27), reporter=r1) # 正向添加-添加记录时需要指定reporter >>> a1.save()
>>> a2 = Article.objects.create(headline="Paul's story", pub_date=date(2006, 1, 17), reporter=r2) # 正向添加-用管理器的方式添加,不需要save()
>>> r2.article_set.create(headline="John's second story", pub_date=date(2005, 7, 29)) # 反向添加-注意r2.article_set的方式
>>> r3 = Reporter(first_name="Jan", last_name="--", email="jan@example.com")
>>> r3.save()
>>> r3.article_set.add(a2) # 反向添加一个对象
>>> r3.artilce_set.remove(a2) # 反向删除
add()和remove()是django默认的用于处理一对多或者多对多的增删操作。对于ForeignKey,只有“一”的那张表可以使用add和remove来一次性添加一个或者多个“多”的记录。对于ManyToManyField,两张表都可以使用add和remove添加或者删除一个或者多个记录。
四、创建多对多表
# models.py from django.db import models class Publication(models.Model): title = models.CharField(max_length=30) def __str__(self): return self.title class Meta: ordering = ('title',) class Article(models.Model): headline = models.CharField(max_length=100) publications = models.ManyToManyField(Publication) def __str__(self): return self.headline class Meta: ordering = ('headline',)
>>> p1 = Publication(title='The Python Journal') >>> p1.save() >>> p2 = Publication(title='Science News') >>> p2.save() >>> p3 = Publication(title='Science Weekly') >>> p3.save()
>>> a1 = Article(headline='Django lets you build Web apps easily')
>>> a1.save()
>>> a1.publications.add(p1,p2,p3) # 直接用add添加一个或多个记录
>>> a2 = Article(headline='NASA uses Python') >>> a2.save()
>>> new_publication = a2.publications.create(title='Highlights for Children') # 或者直接用管理器(models.objects.create)创建一条记录并添加进去
>>> p2.article_set.all() # 可以通过article_set访问
>>> a4 = Article(headline='NASA finds intelligent life on Earth') # 反向添加记录:可以先写好记录,再添加给另一张表 >>> a4.save() >>> p2.article_set.add(a4)
>>> new_article = p2.article_set.create(headline='Oxygen-free diet works wonders') # 或者也用管理器的方式添加
二、Field和字段
名称 | 解释 |
AutoField | 一个根据实际ID自动增长的IntegerField,通常不指定;如果不指定,一个主键字段将自动添加到模型中 |
BooleanField | true/false 字段,此字段的默认表单控制是CheckboxInput |
NullBooleanField | 支持null、true、false三种值 |
CharField(max_length=字符长度) | 字符串,默认的表单样式是 TextInput |
TextField | 大文本字段,一般超过4000使用,默认的表单控件是Textarea |
IntegerField | 整数 |
DecimalField(max_digits=None, decimal_places=None) | 使用python的Decimal实例表示的十进制浮点数;DecimalField.max_digits=位数总数,DecimalField.decimal_places=小数点后的数字位数 |
FloatField | 用Python的float实例来表示的浮点数 |
DateField[auto_now=False, auto_now_add=False]) |
使用Python的datetime.date实例表示的日期; 参数DateField.auto_now:每次保存对象时,自动设置该字段为当前时间,用于"最后一次修改"的时间戳,它总是使用当前日期,默认为false; 参数DateField.auto_now_add:当对象第一次被创建时自动设置当前时间,用于创建的时间戳,它总是使用当前日期,默认为false 该字段默认对应的表单控件是一个TextInput. 在管理员站点添加了一个JavaScript写的日历控件,和一个“Today"的快捷按钮,包含了一个额外的invalid_date错误消息键 auto_now_add, auto_now, and default 这些设置是相互排斥的,他们之间的任何组合将会发生错误的结果 |
TimeField | 使用Python的datetime.time实例表示的时间,参数同DateField |
DateTimeField | 使用Python的datetime.datetime实例表示的日期和时间,参数同DateField |
FileField | 一个上传文件的字段 |
ImageField | 继承了FileField的所有属性和方法,但对上传的对象进行校验,确保它是个有效的image |
通过字段选项,可以实现对字段的约束;在字段对象时通过关键字参数指定。
名称 | 解释 |
null | 如果为True,Django 将空值以NULL 存储到数据库中,默认值是 False |
blank | 如果为True,则该字段允许为空白,默认值是 False;对比:null是数据库范畴的概念,blank是表单验证证范畴的 |
db_column | 字段的名称,如果未指定,则使用属性的名称 |
db_index | 若值为 True, 则在表中会为此字段创建索引 |
default | 默认值 |
primary_key:若为 True | 则该字段会成为模型的主键字段 |
unique | 如果为 True, 这个字段在表中必须有唯一值;唯一索引 |
auto_now | 创建时生成创建时间 |
auto_now_add | 更新时自动更新当前时间 |
choices | admin: 默认元组列表,user_type=[(1, "普通用户"), (2, "VIP用户"), (3, "超级用户")];modesl.IntegerField(choices=user_type);在admin中会自动显示这几个分类,但是对表无影响,可以是任意整数 |
verbose_name | admin: 别名,支持中文 |
editable | admin: 是否可以被编辑 |
error_message | admin: 自定义错误信息 |
help_text | admin: 帮助信息 |
三、自定义管理器
objects是Manager类型的对象,用于与数据库进行交互。当定义模型类时没有指定管理器,则Django会为模型类提供一个名为objects的管理器,当为模型类指定管理器后,django不再为模型类生成名为objects的默认管理器。
管理器是Django的模型进行数据库的查询操作的接口,Django应用的每个模型都拥有至少一个管理器。
自定义管理器类主要用于两种情况:向管理器类中添加额外的方法,或者修改管理器返回的原始查询集:重写get_queryset()方法。
from django.db import models class BookInfoManager(models.Model): """ 可以重写Model中的方法 """ def get_queryset(self): return super(BookInfo, self.get_queryset().filter(isdelete=False)) # 创建BookInfo def create(self, name, pub_date): b = BookInfo() b.name = name b.headline = pub_date b.isdelete = False return b class BookInfo(models.Model): name = models.CharField(max_length=20) headline = models.CharField(default="this's a python book") isdelete = models.BooleanField(default=False) # 自定义管理器被定义时,继承了默认管理器 manager = BookInfoManager() # BookInfo.manager.create(name="Jan")
四、数据库操作
1、多对一操作
1.批量添加
# models.py from django.db import models class Province(models.Model): name = models.CharField(max_length=32) class City(models.Model): name = models.CharField(max_length=32) pro = models.ForeignKey("Province", to_field="id", on_delete=models.CASCADE)
# app01/insert_records.py import os, django # 添加django的环境配置 os.environ.setdefault("DJANGO_SETTINGS_MODULE", "app.settings") django.setup() from app01.models import Province, City city_dict = {"东莞": 2, "深圳": 2, "惠州": 2, "河源": 2, "泰安": 3, "青岛": 3, "济南": 3, "张家口": 1,"邢台": 1} province_dict = {"河北": 1, "山东": 3, "广东": 2,}
province_records_dict = {id: Province(id=id, name=name) for name, id in province_dict.items()} print(province_records_dict) # 先创建Province Province.objects.bulk_create(province_records_dict.values()) # 再创建City city_records_list = [City(pro=province_records_dict[id], name=name) for name, id in city_dict.items()] print(city_records_list) City.objects.bulk_create(city_records_list) print("ok")
2.正向查询与反向查询
# 1.all - 查找全city表中的全部记录,返回所有的querySet City.objects.all() # 正向查询:指定获取的字段,外表需要用外键+双下划线的形式访问外键表中的字段;如果有两个表中的字段,相当于对两个表同时进行操作 City.objects.all().values("id", "name", "pro__id", "pro_name")
# 反向查询:默认会将外键表的模型类小写当做id,相当于city__id,它同样支持表名+双下划线+字段的形式查询
Province.objects.all().values("id", "name", "city", "city__name", "city__id")
# 2.filter - 过滤器
# 正向过滤: 根据某个外键表或者外表中的字段对外表记录进行过滤查询
City.objects.all().filter(pro__name="河北") # 过滤字段
# 反向过滤: 根据某个外键表或者外表中的字段对外键表进行过滤查询
Province.objects.all().filter(name="河北")[0].city_set.all()
3、多对多操作
1.批量添加
# models.py from django.db import models class Book(models.Model): name = models.CharField(max_length=32) class Author(models.Model): name = models.CharField(max_length=32) book = models.ManyToManyField(Book, related_name="book")
import os, django # 添加django的环境配置 os.environ.setdefault("DJANGO_SETTINGS_MODULE", "app.settings") django.setup() from app01.models import * Book.objects.all().delete() Author.objects.all().delete() book_dict = {1: "Python", 2: "Go", 3: "Linux", 4: "PHP"} author_dict = {1: "Jan", 2: "Li", 3: "wise"} together_dict = { "Jan": ["Python", "Go"], "Li": ["Linux", "Go"], "wise": ["PHP", "Go", "Python"] } # 根据以上信息创建ManyToMany # 先创建两个表的记录 book_records_list = {name: Book(id=id, name=name) for id, name in book_dict.items()} Book.objects.bulk_create(book_records_list.values()) author_records_list = {name: Author(id=id, name=name) for id, name in author_dict.items()} Author.objects.bulk_create(author_records_list.values()) # 再根据关系写入数据库 for key, vlist in together_dict.items(): # 获取书名对应的querySet for i in vlist: author_records_list[key].book.add(book_records_list[i])
2、正向查询与反向查询
# 正向查找 Author.objects.get(id=1).book.all() # 反向查找
Book.objects.get(id=1).book.all() # 这里写.book.all()是因为在Author模型类中声明了related_name,默认的话是author_set.all()
Book.objects.get(id=1).book.all().values("id", "name", "book__name")
# 遍历
for item in Author.objects.values("id", "name", "book"):
print(item["id"], item["name"], item["book"])
3、基本查询
1.返回querySet集合的方法
名称 | 解释 |
filter | 过滤器 |
exclude |
排除满足条件的对象 |
order_by | 查询结果排序 |
annotate | 分组函数,在分组后的数据集上进行操作 |
distinct | 去重 |
values | 将querySet以字典形式返回到一个查询集里 |
raw | 处理一个原始的sql语句 |
aggregate | 聚合函数,在每个记录上进行操作 |
2.返回单个对象
名称 | 函数 |
get() | 返回单个满足条件的对象 |
first() | 返回第一个对象 |
last() | 返回最后一个对象 |
exists() | 判断查询集中是否有数据,如果有则返回True |
count() | 返回当前查询的总条数 |
select_related() | 返回当前querySet的前一条和后一条数据 |
3.字段查询
实现where子名,作为方法filter()、exclude()、get()的参数。语法:属性名称__比较运算符=值。
1.exact:表示判等,大小写敏感;如果没有写“ 比较运算符”,表示判等filter(isDelete=False)
2.contains:是否包含,大小写敏感
exclude(btitle__contains='传')
3.startswith、endswith:以value开头或结尾,大小写敏感
exclude(btitle__endswith='传')
4.isnull、isnotnull:是否为null
filter(btitle__isnull=False)
5.在前面加个i表示不区分大小写,如iexact、icontains、istarswith、iendswith
6.in:是否包含在范围内
filter(pk__in=[1, 2, 3, 4, 5])
7.gt、gte、lt、lte:大于、大于等于、小于、小于等于
filter(id__gt=3)
8.对year、month、day、week_day、hour、minute、second:对日期间类型的属性进行运算
filter(bpub_date__year=1980) filter(bpub_date__gt=date(1980, 12, 31))
9.查询的快捷方式:pk,pk表示primary key,默认的主键是id
filter(pk__lt=6)
4、跨表查询
多对一或者多对多都可以使用模型类+双下划线+字段名的形式进行跨表操作。
Author.objects.filter(book__name="Go")
Book.objects.filter(book__name="Jan")
5、聚合函数
使用aggregate()函数返回聚合函数的值,函数包括(Avg,Count,Max,Min,Sum)。
from django.db.models import Max maxDate = list.aggregate(Max('bpub_date'))
6、其它相关
# 1. Cast,用于做类型转换 # v = models.UserInfo.objects.annotate(c=Cast('pwd', FloatField())) # 2. Coalesce,从前向后,查询第一个不为空的值 # v = models.UserInfo.objects.annotate(c=Coalesce('name', 'pwd')) # v = models.UserInfo.objects.annotate(c=Coalesce(Value('666'),'name', 'pwd')) # 3. Concat,拼接 # models.UserInfo.objects.update(name=Concat('name', 'pwd')) # models.UserInfo.objects.update(name=Concat('name', Value('666'))) # models.UserInfo.objects.update(name=Concat('name', Value('666'),Value('999'))) # 4.ConcatPair,拼接(仅两个参数) # v = models.UserInfo.objects.annotate(c=ConcatPair('name', 'pwd')) # v = models.UserInfo.objects.annotate(c=ConcatPair('name', Value('666'))) # 5.Greatest,获取比较大的值;least 获取比较小的值; # v = models.UserInfo.objects.annotate(c=Greatest('id', 'pwd',output_field=FloatField())) # 6.Length,获取长度 # v = models.UserInfo.objects.annotate(c=Length('name')) # 7. Lower,Upper,变大小写 # v = models.UserInfo.objects.annotate(c=Lower('name')) # v = models.UserInfo.objects.annotate(c=Upper('name')) # 8. Now,获取当前时间 # v = models.UserInfo.objects.annotate(c=Now()) # 9. substr,子序列 # v = models.UserInfo.objects.annotate(c=Substr('name',1,2)) # ########### 时间类函数 ########### # 1. 时间截取,不保留其他:Extract, ExtractDay, ExtractHour, ExtractMinute, ExtractMonth,ExtractSecond, ExtractWeekDay, ExtractYear, # v = models.UserInfo.objects.annotate(c=functions.ExtractYear('ctime')) # v = models.UserInfo.objects.annotate(c=functions.ExtractMonth('ctime')) # v = models.UserInfo.objects.annotate(c=functions.ExtractDay('ctime')) # # v = models.UserInfo.objects.annotate(c=functions.Extract('ctime', 'year')) # v = models.UserInfo.objects.annotate(c=functions.Extract('ctime', 'month')) # v = models.UserInfo.objects.annotate(c=functions.Extract('ctime', 'year_month')) """ MICROSECOND SECOND MINUTE HOUR DAY WEEK MONTH QUARTER YEAR SECOND_MICROSECOND MINUTE_MICROSECOND MINUTE_SECOND HOUR_MICROSECOND HOUR_SECOND HOUR_MINUTE DAY_MICROSECOND DAY_SECOND DAY_MINUTE DAY_HOUR YEAR_MONTH """ # 2. 时间截图,保留其他:Trunc, TruncDate, TruncDay,TruncHour, TruncMinute, TruncMonth, TruncSecond, TruncYear # v = models.UserInfo.objects.annotate(c=functions.TruncHour('ctime')) # v = models.UserInfo.objects.annotate(c=functions.TruncDate('ctime')) # v = models.UserInfo.objects.annotate(c=functions.Trunc('ctime','year'))
4、F对象和Q对象
1.F对象
如果需要在等号右侧需要使用当前querySet中的字段,就需要用到F对象。
from django.db.models import F
1.右侧需要用到本表字段,用F对象
Book.objects.all().update(price=F('price') + 20) 2.django支持对F()对象使用算数运算 list.filter(bread__gte=F('bcommet') * 2) 3.F()对象中还可以写作“模型类__列名”进行关联查询 list.filter(isDelete=F('heroinfo__isDelete')) 4.对于date/time字段,可与timedelta()进行运算 list.filter(bpub_date__lt=F('bpub_date') + timedelta(days=1))
2.Q对象
过滤器的方法中关键字参数查询,会合并为And进行;需要进行or查询,使用Q()对象。Q对象(django.db.models.Q)用于封装一组关键字参数,这些关键字参数与“比较运算符”中的相同。
from django.db.models import Q
1.基本操作:
Author.objects.filter(Q(name="Jan")) # 等价于Author.objects.filter(name="Jan")
2.Q对象可以使用&(and)、|(or)操作符组合起来:
Author.objects.filter(Q(name="Jan") | Q(name="Wise")) # 等价于Author.objects.filter(name__in=["Jan", "Wise"])
3.支持联表查询
Author.objects.filter(Q(book__name="Go") & Q(id__lt=2))
3.当操作符应用在两个Q对象时,会产生一个新的Q对象
list.filter(pk_ _lt=6).filter(bcommet_ _gt=10)
list.filter(Q(pk_ _lt=6) | Q(bcommet_ _gt=10))