django模型层
1.基本操作
django 模型层 是Django自定义的一套独特的ORM技术。
1.基本操作
使用django开发的首要任务就是定义模型类及其属性。每个模型类都可以被映射为数据库中的一个数据表,而雷属性被映射为数据字段,除此之外
数据库的主键 外键 约束也通过类属性完成定义
| from django.db import models |
| class ModelName(models.Model): |
| fiels1 = models.XXField(..) |
| fiels2 = models.XXField(..) |
| ... |
| class Meta: |
| db_table = ... |
| pther_metas = ... |
- 所有Django模型继承自django.db.models.Model
- 通过其中的属性定义模型字段,模型字段必须是某种models.XXField类型
- 通过模型类中的Meta子类定义模型元数据,比如数据库表名、数据默认排序方式
Meta类的属性名由Django预定义,常用的Meta类属性汇总如下:
- abstract: True or False 标识其本身是否为抽象类
- app_label:定义本类所属的应用,比如 app_label=[myapp]
- db_table:映射的数据表名,比如db_table = [moments]
如果Meta中不提供db_table字段 ,则Django会为模型自动生成数据表名,生成的格式为[应用名_模型名],比如应用app的模型Comment
的默认数据表名称为app_comment.
- db_tablespace:映射表空间名称,表空间的概念只在某些数据库中如Oracle中存在,不存在表空间概念的数据库将忽略本字段
- default_related_name: 定义本模型的反向关系引用名称,默认与模型名一致。本名称的含义将在后续的内容中说明。
- get_latest_by:定义那个字段值排列以获取模型的开始或结束记录,本属性值通常指向一个日期或整型的模型字段。
- managed:True或False 定义django的manage.py命令工具是否为管理本模型。本属性默认为True,如果将其设为False,则运行
python manage.py migrate时将不会在数据库中生成本模型的数据表,所以需要手工维护数据库的定义
- order_with_respect_to:定义本模型可以按照某外键引用的关系排序
- ordering:本模型记录的默认排序字段,可以设置多个字段,默认以升序排列,如果以降序排列需要在字段前加负号。
比如 如下定义按user_name升序和pub_date 降序
| class Meta: |
| ordering = ['user_name','-pub_date'] |
| |
- default_permissions:模型操作权限,默认为default_permissions = ('add','change','delete')
- proxy:True或False本模型及所有继承自本模型的子模型是否为代理模型
- required_db_features:定义底层数据所必须具备额特性。比如required_db_features=[gis_enabled]只将本数据模型生成在满足gis_enabled特性数据库中
- required_db_vendor:定义底层数据库的类型,比如SQLite,PostgreSQL MySQL Oracle。如果定义了本属性则模型只能在其声明的数据库中维护。
- unique_together:用来设置不重复的字段组合,必须唯一(可以多个字段做联合唯一)
| class Meta: |
| unique_together=(('user_name','pub_date')) |
上述代码定义每个user_name 在同一个pub_date中只能有一条数据表记录。因为unique_together本身是一个元祖,所以可以设置为多个这样的唯一约束。
- index_together:定义联合索引字段,可以设置多个
| class Meta: |
| index_together=[["pub_date",'deadline'],] |
| |
- verbose_name:指明一个易于理解和表述的单数形式的对象名称。如果这个值没有被设置,则Django会将使用该model类名的分词形式作为它的对象表述名,即CamelCase 将会被转换为camel case
- verbose_name_plural:指明一个易于理解和表述的复数形式的对象名称
2.普通字段类型
| 普通字段是指模型类中除外键关系外的数据字段属性。数据字段为Django使用模型时提供如下信息。 |
3常用字段参数
| 每个字段类型都有一些特定的HTML标签和表单验证参数,比如Height_field path。但同时有一些每个字段都可以设置的公共餐胡,比如通过primary_key参数可以设置的一个模型的主键字段: |
| from django.db import models |
| class Moment(models.Model): |
| id = models.AuthField(primary_key=True) |
其他这样的参数如下
- null:定义是否允许相对应的数据库字段为Null,默认设置为False
- balnk:定义字段是否可以为空。blank和null的区别,null是一个非空约束,blank用于字段的HTML表单验证,即判断用户是否可以不输入数据。
- choices:定义字段的可选值。本字段的值应该是一个包含二维元素的元组。元组的每个元素中的第一个值实际存储的值,第二个值是HTML页面中选择时显示的值
| from django.db import models |
| LEVEALS = ( |
| ('1','Very Good'), |
| ('2','Good'), |
| ('3','Normal'), |
| ('4','Bad'), |
| ) |
| class Moment(models.Model): |
| id = models.AutoField(primary_key=True) |
| level = models.CharField(max_length=1,choices=LEVELS) |
| """ |
| 代码中定义了level字段用于让用户选择满意度,其中1 2 3 4 是在数据库中实际存储的数据,而Very good good normal bad等是在HTML的列表控件正提供给用户的选项 |
| """ |
| + default:设定默认值,例如 default='please input here' |
| + help_text:HTML页面中输入控件的帮助字符串。 |
| + primary_key:定义字段是否为主键,为Trye或False |
| + unique:是否为字段定义数据库的唯一约束 |
| 除了这些有名称的字段设置,django中的所有Field数据类型还有一个无名参数,可以设置该字段在HTML页面中人性化的名称 |
| class Comment(models.Model): |
| id = models.AutoField(primary_key=True) |
| level = models.CharField("请为本条信息评级",max_length=1,choices=LEVELS) |
| """ |
| 本例中开发者为level字段定义了任性化名称"请为本条信息评级",如果不设置本参数,这字段名称本身将被显示在HTML页面中作为输入提示 |
| """ |
4.基本查询
定义如下的 Django model 用于演示Django模型的基本查询技术
| from django.db import models |
| class Comment(models.Model): |
| id = models.AutoField(primary_key=True) |
| headline = models.CharField(max_length=255) |
| body_text = models.TextField() |
| pub_date = models.DateField() |
| n_visits = models.IntegerField() |
| def __str__(self): |
| return self.headline |
| |
Django使用objects对象实现模型数据查询,比如查询Comment模型的所有数据
Django有两种过滤器用于筛选记录
- filter(**kwargs):返回符合筛选条件的数据集
- exclude(**kwargs):返回不符合筛选条件的数据集
比如,如下语句用于查询所有pub_date的年字段是2018的Comment:
| Comment.objects.filter(pub_date__year=2018) |
技巧:
多个filter和exclude可以连接在一起查询,比如Comment.objects.filter(pub_date__year=2018).exclude(pub_date__month=1).exclude(n_visits_exact=0) 查询所有2018年非1月的n_visit不为0的记录
pub_date__year 他不是模型中定义的一个字段,而是django中定义的一中独特的字段查询(field lookup)表达方式,本例中该查询的含义是pub_date字段的year属性为2018 field lookup的基本表现形式为字段名称__谓词
Django谓词表
谓词 含义 示例
exact 精确等于 Comment.objects.filter(id__exact=14)
iexact 大小写不敏感等于 Comment.objects.filter(headline_iexact='i like this')
contains 模糊匹配 Comment.objects.filter(headline_contains="good")
in 包含 Comment.objects.filter(id_in=[1,5,9])
gt 大于
gte 大于等于 Comment。objects.filter(n_visits__gte=30)
lt 小于
lte 小于等于
startswith 以...开头 Commet.objects.filter(body_text__startswith="Hello")
endswith 以...结尾
range 在...范围内 start_date=datetime.date(2018,1,1)
end_date = datetime.date(2018,2,1)
Comment.objects.filter(pub_date__range=(start_date,end_date))
year 年
month 月 Comment.objects.filter(pub_date__year=2018)
day 日
week_day 星期几
isnull 是否为空 Comments.obejcts.filter(pub_date__isnull=True)
除了all() filter() exclude()等返回数据集的函数,Django还提供了了get()用于查询单条记录,比如获取id为3的记录
Comment.objects.get(id_exact=1)
Django还提供了了用于查询指定条数的数据集的下边操作,该特性使得Django模型能够支持标准SQL中limit和OFFSET谓词
Comment.objects.all()[:10]
Comment.objects.all()[10:20]
Comment.objects.all()[1]
Comment.objects.order_by('headline')#返回数据集,并按照headline字段排序
5数据保存与删除
Django的一个较大的优势是定义了一个统一的方法save(),用于完成模型的insert和update操作,在执行模型实例的save()函数时django会根据主键判断记录是否存在,如果存在则执行Update操作否则执行insert操作 delete方法用于删除记录,也可以用于数据集,也可以用单条记录
Comments.objects.filter(pub_date__year=2017).delete()
Comments.objects.get(id=3).delete()
2.关系操作
利用数据表之间的关系进行数据建模和业务开发是关系数据库的最主要的功能。django模型层对3种关系(1:1,1:N,M:N)都有强大的支持
1.一对一关系
| from django.db import models |
| class Account(models.Model): |
| user_name = models.CharField(max_length=80) |
| password = models.CharField(max_length=255) |
| reg_date = models.DateField() |
| def __unicode__(self): |
| return f'Account: {self.user_name}' |
| class Contact(models.Model): |
| account = models.OneToOneField( |
| Account, |
| on_delete = models.CASCADE, |
| primary_key=True, |
| ) |
| zip_code = models.CharField(max_length=10) |
| address = models.CharField(max_length=80) |
| mobile = models.CharField(amx_length=20) |
| def __unicode__(self): |
| return f'{self.account.user_name}{mobile}' |
| |
| """ |
| 两个模型的关系通过Contact模型中的account字段进行定义。 |
| OneToOneField()的第一个参数定义被关联的模型名 |
| on_delete 参数定义当被关联模型(Account)的记录被删除时本模型的记录如何处理 models.CASCADE用于定义此时本记录(Contact)也被删除 |
| 每个模型的__unicode__()函数用于定义模型的显示字符串 |
| """ |
| |
| a1 = Account(user_name='david') |
| |
| a1.save() |
| a2 = Account(user_name='Rose') |
| a2.save() |
| |
| c1 = Contact(account=a1,mobile='1331234222') |
| c1.save() |
| |
| print(a1.contact) |
| print(c1.account) |
| |
| print(hasattr(a2,"contact")) |
| |
| a1.delete() |
| |
| |
一对多关系
| 在SQL语言中,1:N关系通过在附表 中设置到主表的外键引用来完成。在django模型层,可以用models.ForeginKey类型的字段定义外键 |
| from django.db import models |
| class Account(models.Model): |
| user_name = models.CharField(max_length=80) |
| password = models.CharField(max_length=255) |
| reg_date = models.DateField() |
| def __unicode__(self): |
| return "Account %s"%self.user_name |
| class Contact(models.Model): |
| account = models.ForeignKey(Account,on_delete = models.CASCADE) |
| zip_code = models.CharField(max_length=10) |
| address = models.CharField(max_length=80) |
| mobile = models.CharField(amx_length=20) |
| def __unicode__(self) |
| return "%s,%s"%(self.account.user_name,mobile) |
| |
| """ |
| 上述代码与一对一关系唯一不同的是用models.ForeignKey 定义了Contact模型中的account字段 。这样每个Account对象就 |
| 可以与多个Contact对象关联了。在一对多关系中,每个主模型对象可以关联多个子对象,所以本例中从主模型Account对象中寻找 |
| 附模型Contact的属性是contact_set 即通过 一个集合返回关联结果 |
| 技巧:xxx_set是django设定的通过主模型对象访问附模型对象集合的属性名 |
| """ |
3.多对多关系
| from django.db import models |
| class Account(models.Model): |
| user_name = models.CharField(max_length=80) |
| password = models.CharField(max_length=255) |
| reg_date = models.DateField() |
| def __unicode__(self): |
| return "Account %s"%self.user_name |
| class Contact(models.Model): |
| account = models.ManyToManyField(Account) |
| zip_code = models.CharField(max_length=10) |
| address = models.CharField(max_length=80) |
| mobile = models.CharField(amx_length=20) |
| def __unicode__(self) |
| return "%s,%s"%(self.account.user_name,mobile) |
| |
| """ |
| 上述代码与一对一关系唯一不同的是用models.ForeignKey 定义了Contact模型中的account字段 。这样每个Account对象就 |
| 可以与多个Contact对象关联了。在一对多关系中,每个主模型对象可以关联多个子对象,所以本例中从主模型Account对象中寻找 |
| 附模型Contact的属性是contact_set 即通过 一个集合返回关联结果 |
| 技巧:xxx_set是django设定的通过主模型对象访问附模型对象集合的属性名 |
| """ |
3面对对象ORM
Django ORM的一个强大指出就是对模型继承的支持,该技术将Python面对对象的编程方法与数据库面向关系表的数据就够有机的结合。
Django支持三种风格的模型继承
- 抽象类继承:父类继承自models.Model,但是不会在底层数据中生成相应的数据表,父类的属性列村粗在其子类的数据表中
- 多表继承:多表继承的每个模型类都在底层数据库中生成相应的数据表管理数据
- 代理模型继承:父类用于在底层数据库中管理数据表 而子类不定义数据列,只定义查询数据集的排序方式等元数据
1.抽象类继承
抽象类继承的作用是在多个表有若干个相同的字段时,可以使开发者将这些字段同意定义在抽象基类中,免于重复定义这些字段。抽象基类的定义通过在模型的Meta中定义属性abstract来实现。抽象基类的举例如下:
| from django.db import models |
| class MessageBase(mdoels.Model): |
| id = models.AutoField() |
| content = models.CharField(max_length=100) |
| user_name = models.CharField(amx_length=80) |
| pub_date = models.DateField() |
| class Meta: |
| abstract=True |
| class Moment(MessageBase): |
| headline = models.CharField(max_length=50) |
| LEVELS = ( |
| ('1','very good'), |
| ('2','good'), |
| ('3','normal'), |
| ('4','bad'), |
| ) |
| class Comment(MessageBase): |
| level = models.CharField(choices = LEVELS,max_length=1) |
| """ |
| 本例中就定义了一个抽象基类MessageBase 用于保存消息的4个公用字段 id content user_name pub_date |
| 子类 Moment Comment 继承自MessageBase 并分别定义自己的一个字段 本例中三个类映射到数据表中之后会被定义为两个数据表 |
| 数据表moment 有 id user_name content pub_date headline 5个字段 |
| 数据表Comment 有id user_name content pub_date level 5个字段 |
| 在子类模型中直接可以饮用父类定义的字段 |
| m1 = Moment(user_name='gyz',headline='hello')#新建Moment对象 |
| m1.content = 'reference parent field in subclass' |
| a1.save() |
| """ |
2.多表继承
在多表继承技术中,无论是父表还是子表都会用数据库中相对应的数据表维护模型数据,父类中的字段不会重复的在多个子类的相关数据表中进行定义。从这种意义上讲,多表继承才是真正面向对象的ORM技术
多表继承的定义不需要特殊的关键字。在django内部通过在父模型和子模型之间建立一对一的关系来实现多表继承技术。如下代码定义了MessageBase及其子类
| from django.db import models |
| class MessageBase(mdoels.Model): |
| id = models.AutoField() |
| content = models.CharField(max_length=100) |
| user_name = models.CharField(amx_length=80) |
| pub_date = models.DateField() |
| |
| class Moment(MessageBase): |
| headline = models.CharField(max_length=50) |
| LEVELS = ( |
| ('1','very good'), |
| ('2','good'), |
| ('3','normal'), |
| ('4','bad'), |
| ) |
| class Comment(MessageBase): |
| level = models.CharField(choices = LEVELS,max_length=1) |
| """ |
| 本例中会在数据库中实际生成3个数据表 |
| 数据表MessageBase:有id content user_name pub_date 4个字段 |
| 数据表 Moment 有id headline两个字段 |
| 数据表 Comment 有id 和level两个字段 |
| 在对模型的编程过程中,子类仍然可以直接引用父类定义的字段同时子类可以通过父类对象应用访问父类实例 |
| #新建Moment对象 直接在子类中引用父类字段 |
| m1 = Moment(user_name="Terry",headline="hello world") |
| m1.content = "reference parent field in subclass" |
| a1.save() |
| #通过父类实例引用父类字段 |
| print(m1.messagebase.content) |
| 技巧:多表继承时 在子类事例中通过小写的父类名称可以引用父类的实例 |
| """ |
3代理模型继承
在前两种继承模型中子类模型都有实际的存储数据的作用;而在代理模型继承中子类只用于管理父类的数据,而不是实际存储数据。代理模型继承通过在子类的Meta中定义proxy=True 属性来实现,举例如下:
| |
| from django.db import models |
| class Moment(models.Model): |
| id = models.AutoField() |
| headline = models.CharField(max_length=50) |
| content = models.CharField(max_length=100) |
| user_name = models.CharField(amx_length=80) |
| pub_date = models.DateField() |
| class OrderedMoment(Moment): |
| class Meta: |
| proxy=True |
| ordering = ["-pub_date"] |
| """ |
| 在本例中定义父类模型Moment用于存储数据,而后定义了子类模型OrderedMomnet用于管理根据pub_date倒序排列的Moment 使用代理模型继承的原因是子类中新的特性不会影响父类模型及其已有代码的行为 |
| |
| """ |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)