编辑
2024-07-07
👷后端架构
00
请注意,本文编写于 347 天前,最后修改于 225 天前,其中某些信息可能已经过时。

目录

1. 基本操作
1.1 模型类定义
1.2 普通字段类型
1.3 常用字段参数
1.4 基本查询
1.5 数据保存与删除
2. 关系操作
2.1 一对一关系
2.2 一对多关系
2.3 多对多关系
3. 面向对象ORM
3.1 抽象类继承
3.2 多表继承
3.3 代理模型继承

1. 基本操作

1.1 模型类定义

from django.db import models class ModelName(models.Model): field1 = models.xxField(……) field2 = models.xxField(……) class Meta: db_table = …… other_metas = ……

解析:

  • 所有Django模型继承自django.db.Model类
  • 通过其中的类属性定义模型字段,模型字段必须是某种models.XXField类型
  • 通过模型类的Meta子类定义模型元数据,比如数据库表名、数据库默认排序方式等

Meta类的属性由Django预定义,常用的Meta类属性如下:

  • abstract: True or False 标识本类是否为抽象基类

  • app_label: 定义本类所属的应用,比如app_lable='app'

  • db_table: 映射的数据表名,比如db_table='moments'

    如果Meta中不提供db_table字段,则Django会为模型自动生成数据表名,生成的格式为"应用名_模型名"

  • db_tablespace: 映射的表空间名称。表空间的概念只在某些数据库如Oracle中存在,不存在表空间概念的数据库将忽略本字段

  • default_related_name: 定义本模型的反向关系引用名称,默认与模型名一致。

  • get_latest_by: 定义按那个字段值进行排列以获得模型的开始或结束的记录,本属性通常指向一个日期或整型的模型字段

  • managed: True or False ,定义Django的manage.py 命令行工具是否管理本模型。本属性默认为True,如果将其设为False,则运行python manage.py migrate时不会在数据库中生成本模型的数据表,所以需要手工维护数据库的定义

  • order_with_resect_to: 定义本模型可以按照某外检引用的关系排序

  • ordering: 本模型记录的默认排序字段,可以设置多个字段,默认以升序排列,如果以降序排列则需要再字段名钱加"负号",比如

python
class Meta: ordering=['user_name','-pub_date']
  • default_permissions: 默认操作权限,默认为default_permisstions=('add','change','delete')
  • proxy: True or False , 本模型所有继承自本模型的子模型是否为代理模型
  • required_db_features: 定义底层数据库有所必须具备的特性。比如require_db_features=['gis_enabled'],只将本数据模型生成在gis_enabled特性的数据库中
  • required_db_vendor: 定义底层数据库的类型,比如SQLite、 PostgreSQL、 MySQL、Oracle,如果定义了本属性,则模型只能在其声明的数据库中被维护
  • unique_together: 用来设置的不重复的字段组合,必须唯一(可以将多个字段做联合唯一)
python
class Meta: unique_together = (("user_name","pub_date")) # 上述意识是定义每个user_name在同一个pub_date中只能有一条数据表记录。
  • index_together: 定义联合索引的字段,可以设置多个:
class Meta: index_together = [["pub_date","deadline"],]
  • verbose_name: 知名一个易于理解和表述的单数形式的对象名称。如果这个值没有被设置,则Django将会使用该model的类名的粉刺形式作为对象的表述名
  • Verbose_name_plural: 指明一个易于理解和表述的复数形式的对象名称

1.2 普通字段类型

普通字段是指模型类中除外键关系外的数据字段属性,所有数据字段的属性必须集成自抽象类django.db.models.Field,开发者可以定义自己的继承自该类的字段类型。

字段类型说明
AutoField一个自动递增的整型字段,添加记录时他会自动增长。通常只用于充当数据表的主键,如果没有,django会自动添加一个
BigIntegreField64位整型字段
BinaryField二进制数据字段,只能通过bytes对其进行赋值
BooleanField布尔字段,相对应的Html标签是
CharField字符串字段,用于较短的字符串,相对应的Html标签是单行输入框 input type="text">"
TextField大容量文本字段,相对应的HTML标签是多行编辑框 textarea>"
CommaSeparatedIntegerField用于存放分号分割的整数值,相对于普通的CharField,它有特殊的表单数据验证要求
DateField日期字段,相对应的HTML标签是 input type="text">、一个JavaScript日历和一个"Today"快捷键。有以下额外的可选参数
,当对象被保存时,将该字段的指设置为当前时间
,当对象首次被创建时,将该字段的值设置为当前时间
DateTimeField类似于DateField,同时支持时间的输入
DurationField存储时间周期,用python的timedelta类型构建
EmailField一个带有检查的Email合法性的CharField
FileField一个文件上传字段。在定义本字段时必须传入参数upload_to,用户保存上载文件的服务器文件系统的路径。这个路径必须包含strftime formatting,该格式将被上载的文件date/time替换
FilePathField按目录限制规则选择文件,定义本字段时必须传入参数path
FloatFiled浮点型字段,定义本字段时必须传入参数max_digits和decimal_places,用以定义总位数(不包括小数点和符号)和小整位数
ImageField类似FileField,同时验证上传对象是否是一个合法图片。他有两个可选参数,即height_field和width_field,如果提供这两个参数,则图片将按照提供的高度和宽度保存,该字段要求安装Python Imaging
IntegerField用于保存一个整数
IPAddressField一个字符串形式的IP地址
NullBooleanField类似于BooleanField,但比其多一个None选项
PhoneNumberField带有美国风格的电话号码校验
PositiveIntegerField只能输入非负数的IntegerField
SlugField只包含字母、数字、下划线和连字符的输入字段,它通常用于URL
SmallIntegerField类似于IntegerField,但只具有较小的输入范围,具体范围依赖所使用的数据库
TimeField时间字段,类似于DateTimeField,但只能表达和输入时间
URLField用于保存URL
USSSateField美国州的缩写字段

1.3 常用字段参数

每个字段类型都有一些特定的HTML标签和表单验证参数,比如height_field、path等。但同时有一些每个字段都可以设置的公共参数,比如:

  • primary_key: 通过primary_key参数可以设置一个模型的主键字段
  • null: 定义是否允许相对应的数据库字段为Null,默认设置为False
  • blank: 定义字段是否可以为空。读者需要区分blank与null的区别,null是一个数据库的中的非空约束;而blank用于字段的HTML表单验证,即判断用户是否可以不输入数据。
  • choices: 定义字段的可选值。本字段的值是一个包含二维元素的元组。元组的每个元素中的第1个值是实际存储的值,第2个值是HTML页面页面中进行选择时显示的值。
python
from django.db import models LEVELS=( ('1','very good'), ('2','Good'), ('3','Normal'), ('4','Bad'), ) class Comment(models.Model): id = models.AutoField(primary_key=True) level = models.CharField(max_length=1,choices=LEVELS)
  • default: 设定默认值,例如default='please input here'
  • help_text: HTML页面中输入控件的帮助字符串
  • unique: 是否为字段定义数据库的唯一约束

除了这些有名称的字段参数,django中的所有Field数据类型还有一个无名参数,可以设置该字段在HTML页面中的人性化名称,比如:

python
class Comment(models.Model): id = models.AutoField(primary_key=True) level = models.CharField("请为本条信息评级",max_length=1,choices=LEVELS)

1.4 基本查询

python
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

查询Comment模型的所有数据

Comment.objects.all()

获取单条记录;

Comment.objects.get(id_exact=1)

排序操作

Comment.objects.order_by('head_line')

Django两种过滤器用于筛选记录:

  • filter(*kwargs) 返回符合筛选条件的数据集
  • exclude(**kwargs) 返回不符合筛选条件的数据集

多个filter和exclude可以连接在一起查询

Comment.objects.filter(pub_date__year=2015)

其中的pub_date__year,它不是模型中定义的一个字段,而是Django的定义的一种独特的字段查询(field lookup)表达方式,本例中该查询的含义是"pub_date字段的year属性为2015".

field lookup基本的表现形式是

字段名称__谓词

常见的谓词有如下

谓词含义示例等价的SQL语句
exact精确等于Comment.objects.filter(id__exact=14)select * from Comment where id=14
iexact大小写不敏感的等于Comment.objects.filter(headline__iexact='I like this')select * from Comment where upper(headline)='I LIKE THIS'
contains模糊匹配Comment.objects.filter(headline__contains="good")select * from Comment where headline like "%good%"
in包含Comment.objects.filter(id__in=[1,5,9])select * from Comment where id in (1,5,9)
gt/gte/lt/lte大于Comment.objects.filter(n_visits__gt=30)select * from Comment where n_visits >30
startswith以……开头Comment.objects.filter(body_text__startwith="Hello")select * from Comment where body_text like 'Hello%'
endswith以……结尾Comment.objects.filter()
range在……范围内Comment.objects.filter(pub_date__range=(start_date,end_date))
year/month/day/week_day年/月/日/星期几Comment.objects.filter(pub_date__year=2015)
isnull是否为空Comment.objects.filter(pub_date__isnull=True)select * from Comment where pub_date is NUll

1.5 数据保存与删除

与传统SQL相比,Django的一个较大优势是定义了一个统一的方法save(),用于完成模型的Insert和Update模型。在执行模型实例的save()函数时,Django会根据模型的主键,判断记录是否存在,如果存在则执行Update操作,否则执行Delete操作。比如:

shell
# 新增记录 newObj = Comment(headline="I like this",body_text="……",pub_date=datetime.datetime.now(),n_visits=0) newObj.save() # 打印新增对象的主键 print newObject.id # 修改记录数据 newObj.body_text="This comment is just what I want" newObj.save() # 打印主键,与新增后的id相同 print newObj.id # 删除所有2014的记录 Comment.objects.filter(pub_date__year=2014).delete() # 删除id=3的单条记录 Comment.objects.get(id=3).delete()

2. 关系操作

利用数据表之间的关系进行数据建模和业务开发是关系数据库最主要的功能。

2.1 一对一关系

在SQL语言中,一对一关系通过在两个表之间定义相同的主键来完成。如下代码在模型Account和Contact之间定义了一对一关系

python
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.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(max_length=20) def __unicode__(self): return "%s, %s"%(self.account.user_name,self.mobile)
  • on_delete参数定义当被关联模型的记录被删除时本模型的记录如何处理,models.CASCADE用于定义此时本记录(Contact)也被删除
  • 每个模型的__unicode__()函数用于定义模型的显示字符串
shell
>>> a1 = Account(user_name="david") >>> a1.save() >>> a2 = Account(user_name="Rose") >>> a2.save() # 用a1初始化Contact的account字段,并保存 >>> c1 = Contact(account=a1,mobile="13912345000") >>> c1.save() >>> print a1 <Account: david> >>> print c1 <Contact: david,13912345000> >>> print a1.contact <Contact: david,13912345000> >>> print c1.account <Account: david>

2.2 一对多关系

在SQL语言中,1:N关系通过在"附表"中设置到"主表"的外键引用来完成,在Django模型层,可以用models.ForeignKey类型的字段定义外键

python
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(max_length=20) def __unicode__(self): return "%s, %s"%(self.account.user_name,self.mobile)

对模型的操作代码:

shell
>>> a1 = Account(user_name="Rose") >>> a1.save() >>> c1 = Contact(account=a1,mobile="13912345001") >>> c1.save() >>> c2 = Contact(account=a1,mobile="13912345002") >>> c2.save() >>>print c1.account <Account: Rose> >>>print c2.account <Account: Rose> >>>print a1.contact_set [<Contact: Rose, 13912345001>,<Contact: Rose, 13912345002>] >>>print a1.contact_set.count() 2

在一对多关系中,每个主模型对象可以关联多个子对象,所以本例中从主模型Account对象中寻找附模型Contact的属性是contact_set,即通过一个集合返回结果

2.3 多对多关系

在SQL语言找那个,M

关系通过建立一个中间关系表来完成,该中间表中定义了到两个主表的外键。在Django模型层中,可以选择用两个1
关系来定义M:N关系。这种方式同样通过models.ForeignKey来实现

python
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): accounts = models.ManyToManyField(Account) zip_code = models.CharField(max_length=10) address = models.CharField(max_length=80) mobile = models.CharField(max_length=20) def __unicode__(self): return "%s, %s"%(self.account.user_name,self.mobile)

模型操作:

shell
>>> a1 = Account(user_name="Leon") >>> a1.save() >>> c1 = Contact(mobile="13912345003") >>> c1.save() >>> c1.accounts.add(a1) >>> a2 = Account(user_name="Terry") >>> a2.save() >>> c1.accounts.add(a2) >>> a3 = Account(user_name="Leon") >>> c1.accounts.add(a3) # 这时候就会报错,因为a3还有保存呢 >>> a1.contact_set.remove(c1) # 取消单个对象关联 >>> a1.contact_set.clear() # 取消a1与所有其他Contact对象的关联

3. 面向对象ORM

Django模型层ORM的一个强大之处是对模型继承的支持,该技术将Python面向对象的编程方法与数据库面向关系表的数据结构有机地结合。Django支持三种风格的模型继承:

  • 抽象类继承:父类继承自models.Model,但不会在底层数据库中生成相应的数据表。父类的属性列存储在其子类的数据表中
  • 多表继承: 多表继承的每个模型类都在底层数据库中生成响应的数据表管理数据
  • 代理模型继承:父类用于在底层数据库中管理数据表;而自雷不定义数据列,只定义查询数据集的排序方式等元数据。

3.1 抽象类继承

抽象类继承的作用是在多个表有若干相同的字段时,可以使开发者将这些字段统一定义在抽象基类,免于重复定义这些字段。抽象基类的定义通过在模型的Meta中定义属性abstract=True来实现。代码如下:

python
from django.db import models class MessageBase(models.Model): id = models.AutoField() content = models.CharField(max_length=100) user_name = models.CharField(max_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(max_length=1,choices=LEVELS)

此时,Moment和Comment的数据库,都是有了MessageBase的字段。在子类模型的编程中,可以直接引用父类定义的字段,比如有:

shell
m1 = Moment(user_name="Terry",headline="hello world") # 新建Moment对象 m1.content = "reference parent field in subclass" m1.save()

3.2 多表继承

在多表继承技术中,无论是父表还是子表都会用数据库中相对应的数据表维护模型数据,父类中的字段不会重复地在多个子类的相关数据表中定义。从某种意义上来讲,多表继承才是真正面向对象的ORM技术

多表继承的定义不需要特殊的关键字

python
from django.db import models class MessageBase(models.Model): id = models.AutoField() content = models.CharField(max_length=100) user_name = models.CharField(max_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(max_length=1,choices=LEVELS)

3个数据表:

  • MessageBase 有id content user_name pub_date
  • Moment 有id headline
  • Comment 有id level

在对模型的编程过程中,子类仍然可以直接引用父类定义的字段;同时子类可以通过父类对象引用访问父类示例,比如:

shell
>>>m1 = Moment(user_name="Terry",headline="hello world") # 新建Moment对象 >>>m1.content = "reference parent field in subclass" >>>m1.save() >>>print m1.messagebase.content reference parent field in subclass # 在多表继承时,子类实例中通过小写的父类的名字可以引用父亲的实例

3.3 代理模型继承

在前联众继承模型中子类模型都有实际存储数据的作用;而代理模型中继承中子类只用于管理父类的数据,而不实际存储数据。代理模型继承通过在子类的Meta中定义proxy=True属性来实现.

python
from django.db import models class Moment(models.Model): id = models.AutoField() content = models.CharField(max_length=100) user_name = models.CharField(max_length=80) pub_date = models.DateField() head_line = models.CharField(max_length=50) class OrderedMoment(Moment): class Meta: proxy = True ordering = ["-pub_date"]

本文作者:Eric

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!