ORM基础之ORM介绍和基础操作
一、ORM介绍 1、ORM概念 对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。 简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系数据库中。 ORM在业务逻辑层和数据库层之间充当了桥梁的作用。 2、ORM由来 让我们从O/R开始。字母O起源于"对象"(Object),而R则来自于"关系"(Relational)。 几乎所有的软件开发过程中都会涉及到对象和关系数据库。在用户层面和业务逻辑层面,我们是面向对象的。当对象的信息发生变化的时候,我们就需要把对象的信息保存在关系数据库中。 按照之前的方式来进行开发就会出现程序员会在自己的业务逻辑代码中夹杂很多SQL语句用来增加、读取、修改、删除相关数据,而这些代码通常都是重复的。 3、ORM的优势 ORM解决的主要问题是对象和关系的映射。它通常把一个类和一个表一一对应,类的每个实例对应表中的一条记录,类的每个属性对应表中的每个字段。 ORM提供了对数据库的映射,不用直接编写SQL代码,只需像操作对象一样从数据库操作数据。 让软件开发人员专注于业务逻辑的处理,提高了开发效率。 4、ORM的劣势 ORM的缺点是会在一定程度上牺牲程序的执行效率。 ORM用多了SQL语句就不会写了,关系数据库相关技能退化... 5、ORM总结 ORM只是一种工具,工具确实能解决一些重复,简单的劳动。这是不可否认的。 但我们不能指望某个工具能一劳永逸地解决所有问题,一些特殊问题还是需要特殊处理的。 但是在整个软件开发过程中需要特殊处理的情况应该都是很少的,否则所谓的工具也就失去了它存在的意义。 二、Django中的ORM 1、Django项目使用MySQL数据库 1. 手动新建一个数据库 2. 在Django项目的settings.py文件中,配置数据库连接信息: DATABASES = { "default": { "ENGINE": "django.db.backends.mysql", "NAME": "你的数据库名称", # 需要自己手动创建数据库 "USER": "数据库用户名", "PASSWORD": "数据库密码", "HOST": "数据库IP", "POST": 3306 } } 3. 告诉Django用什么模块连接MySQL,在和settings.py同级的__init__.py中: import pymysql pymysql.install_as_MySQLdb() 4. 在app下面的models.py中创建类,类一定要继承models.Model 5.执行两个命令 1. python manage.py makemigrations --> 判断models.py中是否有改动,把改动记录到migrations目录下 2. python manage.py migrate --> 把改动翻译成SQL语句去数据库执行 注意:当项目中不止有一个APP时,python manage.py makemigrations appname 可以单独判断某个app里面的models.py的变更 2、Model 在Django中model是你数据的单一、明确的信息来源。它包含了你存储的数据的重要字段和行为。通常,一个模型(model)映射到一个数据库表 基本情况: 每个模型都是一个Python类,它是django.db.models.Model的子类。 模型的每个属性都代表一个数据库字段。 综上所述,Django为您提供了一个自动生成的数据库访问API。、 3、说明 ORM定义表 from django.db import models class Book(models.Model): title = models.CharField(max_length=20) publisher = models.CharField(max_length=30) title 和 publisher 是模型的字段。每个字段被指定为一个类属性,每个属性映射到一个数据库列。 上面的模型等于下面的SQL建表语句 CREATE TABLE myapp_book ( "id" serial NOT NULL PRIMARY KEY, "title" varchar(20) NOT NULL, "publisher" varchar(30) NOT NULL ); 表myapp_book的名称是自动生成的,如果你要自定义表名,需要在model的Meta类中指定 db_table 参数,强烈建议使用小写表名,特别是使用MySQL作为后端数据库时。 id字段是自动添加的,如果你想要指定自定义主键,只需在其中一个字段中指定 primary_key=True 即可。如果Django发现你已经明确地设置了Field.primary_key,它将不会添加自动ID列。 Django会根据配置文件中指定的数据库后端类型来生成相应的SQL语句。 Django支持MySQL5.5及更高版本。 4、字段 总览
AutoField(Field) - int自增列,必须填入参数 primary_key=True BigAutoField(AutoField) - bigint自增列,必须填入参数 primary_key=True 注:当model中如果没有自增列,则自动会创建一个列名为id的列 from django.db import models class UserInfo(models.Model): # 自动创建一个列名为id的且为自增的整数列 username = models.CharField(max_length=32) class Group(models.Model): # 自定义自增列 nid = models.AutoField(primary_key=True) name = models.CharField(max_length=32) SmallIntegerField(IntegerField): - 小整数 -32768 ~ 32767 PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField) - 正小整数 0 ~ 32767 IntegerField(Field) - 整数列(有符号的) -2147483648 ~ 2147483647 PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField) - 正整数 0 ~ 2147483647 BigIntegerField(IntegerField): - 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807 BooleanField(Field) - 布尔值类型 NullBooleanField(Field): - 可以为空的布尔值 CharField(Field) - 字符类型 - 必须提供max_length参数, max_length表示字符长度 TextField(Field) - 文本类型 EmailField(CharField): - 字符串类型,Django Admin以及ModelForm中提供验证机制 IPAddressField(Field) - 字符串类型,Django Admin以及ModelForm中提供验证 IPV4 机制 GenericIPAddressField(Field) - 字符串类型,Django Admin以及ModelForm中提供验证 Ipv4和Ipv6 - 参数: protocol,用于指定Ipv4或Ipv6, 'both',"ipv4","ipv6" unpack_ipv4, 如果指定为True,则输入::ffff:192.0.2.1时候,可解析为192.0.2.1,开启此功能,需要protocol="both" URLField(CharField) - 字符串类型,Django Admin以及ModelForm中提供验证 URL SlugField(CharField) - 字符串类型,Django Admin以及ModelForm中提供验证支持 字母、数字、下划线、连接符(减号) CommaSeparatedIntegerField(CharField) - 字符串类型,格式必须为逗号分割的数字 UUIDField(Field) - 字符串类型,Django Admin以及ModelForm中提供对UUID格式的验证 FilePathField(Field) - 字符串,Django Admin以及ModelForm中提供读取文件夹下文件的功能 - 参数: path, 文件夹路径 match=None, 正则匹配 recursive=False, 递归下面的文件夹 allow_files=True, 允许文件 allow_folders=False, 允许文件夹 FileField(Field) - 字符串,路径保存在数据库,文件上传到指定目录 - 参数: upload_to = "" 上传文件的保存路径 storage = None 存储组件,默认django.core.files.storage.FileSystemStorage ImageField(FileField) - 字符串,路径保存在数据库,文件上传到指定目录 - 参数: upload_to = "" 上传文件的保存路径 storage = None 存储组件,默认django.core.files.storage.FileSystemStorage width_field=None, 上传图片的高度保存的数据库字段名(字符串) height_field=None 上传图片的宽度保存的数据库字段名(字符串) DateTimeField(DateField) - 日期+时间格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] DateField(DateTimeCheckMixin, Field) - 日期格式 YYYY-MM-DD TimeField(DateTimeCheckMixin, Field) - 时间格式 HH:MM[:ss[.uuuuuu]] DurationField(Field) - 长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型 FloatField(Field) - 浮点型 DecimalField(Field) - 10进制小数 - 参数: max_digits,小数总长度 decimal_places,小数位长度 BinaryField(Field) - 二进制类型
常用字段 1. AutoField() ***** - int自增列,必须填入参数 primary_key=True - 当model中如果没有自增列,则自动会创建一个列名为id的列(即自增的id主键) 2. IntegerField ***** - 整数列(有符号的) -2147483648 ~ 2147483647 3. BooleanField - 布尔值类型 4. CharField ***** - 字符类型varchar - 必须提供max_length参数, max_length表示字符长度 5. TextField - 文本类型 6. EmailField - 字符串类型,Django Admin以及ModelForm中提供验证机制 7. GenericIPAddressField - 字符串类型,Django Admin以及ModelForm中提供验证 Ipv4和Ipv6 - 参数: protocol,用于指定Ipv4或Ipv6, 'both',"ipv4","ipv6" unpack_ipv4, 如果指定为True,则输入::ffff:192.0.2.1时候,可解析为192.0.2.1,开启此功能,需要protocol="both" 8. UUIDField ***** - 字符串类型,Django Admin以及ModelForm中提供对UUID格式的验证 9. FileField - 字符串,路径保存在数据库,文件上传到指定目录 - 参数: upload_to = "" 上传文件的保存路径 storage = None 存储组件,默认django.core.files.storage.FileSystemStorage 10. ImageField - 字符串,路径保存在数据库,文件上传到指定目录 - 参数: upload_to = "" 上传文件的保存路径 storage = None 存储组件,默认django.core.files.storage.FileSystemStorage width_field=None, 上传图片的高度保存的数据库字段名(字符串) height_field=None 上传图片的宽度保存的数据库字段名(字符串) 11. DateTimeField ***** - 日期+时间格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] 12. DateField ***** - 日期格式 YYYY-MM-DD 13. TimeField - 时间格式 HH:MM[:ss[.uuuuuu]] 14. FloatField - 浮点型 15. DecimalField ***** - 10进制小数 - 参数: max_digits,小数总长度 decimal_places,小数位长度 5、自定义字段 1.自定义char类型字段: from django.db import models class MycharField(models.Field): """ 自定义的char类型的字段类 """ def __init__(self, max_length, *args, **kwargs): self.max_length = max_length super(MycharField, self).__init__(max_length=max_length, *args, **kwargs) def db_type(self, connection): """ 限定生成数据库表的字段类型为char,长度为length指定的值 """ return 'char(%s)' % self.max_length class UserInfo(models.Model): name = MycharField(max_length=12) 2.自定义无符号整数字段: class UnsignedIntegerField(models.IntegerField): def db_type(self, connection): return 'integer UNSIGNED' 3.返回值为字段在数据库中的属性,Django字段默认的值为:
'AutoField': 'integer AUTO_INCREMENT', 'BigAutoField': 'bigint AUTO_INCREMENT', 'BinaryField': 'longblob', 'BooleanField': 'bool', 'CharField': 'varchar(%(max_length)s)', 'CommaSeparatedIntegerField': 'varchar(%(max_length)s)', 'DateField': 'date', 'DateTimeField': 'datetime', 'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)', 'DurationField': 'bigint', 'FileField': 'varchar(%(max_length)s)', 'FilePathField': 'varchar(%(max_length)s)', 'FloatField': 'double precision', 'IntegerField': 'integer', 'BigIntegerField': 'bigint', 'IPAddressField': 'char(15)', 'GenericIPAddressField': 'char(39)', 'NullBooleanField': 'bool', 'OneToOneField': 'integer', 'PositiveIntegerField': 'integer UNSIGNED', 'PositiveSmallIntegerField': 'smallint UNSIGNED', 'SlugField': 'varchar(%(max_length)s)', 'SmallIntegerField': 'smallint', 'TextField': 'longtext', 'TimeField': 'time', 'UUIDField': 'char(32)',
6、字段参数
null 数据库中字段是否可以为空 db_column 数据库中字段的列名 default 数据库中字段的默认值 primary_key 数据库中字段是否为主键 db_index 数据库中字段是否可以建立索引 unique 数据库中字段是否可以建立唯一索引 unique_for_date 数据库中字段【日期】部分是否可以建立唯一索引 unique_for_month 数据库中字段【月】部分是否可以建立唯一索引 unique_for_year 数据库中字段【年】部分是否可以建立唯一索引 verbose_name Admin中显示的字段名称 blank Admin中是否允许用户输入为空 editable Admin中是否可以编辑 help_text Admin中该字段的提示信息 choices Admin中显示选择框的内容,用不变动的数据放在内存中从而避免跨表操作 如:sex = models.IntegerField(choices=[(1, '男'), (2, '女'), (3, '保密')], default=3) error_messages 自定义错误信息(字典类型),从而定制想要显示的错误信息; 字典健:null, blank, invalid, invalid_choice, unique, and unique_for_date 如:{'null': "不能为空.", 'invalid': '格式错误'} validators 自定义错误验证(列表类型),从而定制想要的验证规则 from django.core.validators import RegexValidator from django.core.validators import EmailValidator,URLValidator,DecimalValidator,\ MaxLengthValidator,MinLengthValidator,MaxValueValidator,MinValueValidator 如: test = models.CharField( max_length=32, error_messages={ 'c1': '优先错信息1', 'c2': '优先错信息2', 'c3': '优先错信息3', }, validators=[ RegexValidator(regex='root_\d+', message='错误了', code='c1'), RegexValidator(regex='root_112233\d+', message='又错误了', code='c2'), EmailValidator(message='又错误了', code='c3'), ] )
常用字段的参数 1.null 数据库中字段是否可以为空 2.db_column 数据库中字段的列名 3.default 数据库中字段的默认值 4.primary_key 数据库中字段是否为主键 5.db_index 数据库中字段是否可以建立索引 6.unique 数据库中字段是否可以建立唯一索引 7.unique_for_date 数据库中字段【日期】部分是否可以建立唯一索引 8.unique_for_month 数据库中字段【月】部分是否可以建立唯一索引 9.unique_for_year 数据库中字段【年】部分是否可以建立唯一索引 10.DatetimeField和Datefield独有的参数: auto_now_add = True --> 当前数据的创建时间 auto_now = True --> 当前数据的最后修改时间 注意:auto_now_add和auto_now 不能写在同一个字段上 11. 带choice参数的字段 get_字段名_display() --> 获取choice字段的显示值 例如: sex = models.IntegerField(choices=[(1, '男'), (2, '女'), (3, '保密')], default=3) get_sex_display() --> 保密 12. 建表的元信息 class Meta: db_table = '表名' unique_together = (('ip', 'port'),) index_together = (("pub_date", "deadline"),) 7、元信息 class UserInfo(models.Model): nid = models.AutoField(primary_key=True) username = models.CharField(max_length=32) class Meta: # 数据库中生成的表名称默认是app名称 + 下划线 + 类名(全小写) # 可以通过db_table进行修改 db_table = "userinfo" # 告诉Django这个表名就叫userinfo,不要默认建成 app名称_userinfo # 联合索引 index_together = [ ("pub_date", "deadline"), ] # 联合唯一索引 unique_together = (("ip", "port"),) # admin中显示的表名称 verbose_name # verbose_name加s verbose_name_plural 例如: # 博客表 class Blog(models.Model): title = models.CharField(max_length=32) push_time = models.DateTimeField(auto_now_add=True) # 创建时间 edit_time = models.DateTimeField(auto_now=True) # 最后修改时间 class Meta: db_table = 'blog' # 控制建表名称 # 应用程序表 class Size(models.Model): ip = models.GenericIPAddressField() port = models.IntegerField() class Meta: unique_together = (('ip', 'port'),) 三、ORM的相关操作 小知识:
去manage.py把__name__的下一行,一直到最上面的那几行代码复制到一个py文件, import django django.setup() from appname.models import TableName 测试内容
UserInfo表:
class UserInfo(models.Model): name = MycharField(max_length=12) birthday = models.DateTimeField(verbose_name='生日', null=True) sex = models.IntegerField(choices=[(1, '男'), (2, '女'), (3, '保密')], default=3) def __str__(self): return self.name
1、基本操作13条(必会) 1-1、返回QuerySet对象(列表)的方法(返回的是列表,列表中的元素是对象) all() 查询所有结果 filter(**kwargs) 它包含了与所给筛选条件相匹配的对象 exclude(**kwargs) 它包含了与所给筛选条件不匹配的对象 order_by(*field) 对查询结果排序 reverse() 对查询结果反向排序,请注意reverse()通常只能在具有已定义顺序的QuerySet上调用(在model类的Meta中指定ordering或调用order_by()方法)。 distinct() 从返回结果中剔除重复纪录(如果你查询跨越多个表,可能在计算QuerySet时得到重复的结果。此时可以使用distinct(),注意只有在PostgreSQL中支持按字段去重。) 1-2、特殊的QuerySet(返回的是列表,但列表中的元素不是对象) values(*field) 返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列model的实例化对象,而是一个可迭代的字典序列
User.objects.all().values('id', 'name')
QuerySet [{'id': 1, 'name': '小明'}, {'id': 2, 'name': '小东'}] values_list(*field) 它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列
User.objects.all().values_list('id')
QuerySet [(1, '小明'), (2, '小东')] 1-3、返回具体对象的 get(**kwargs) 返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。 first() 返回第一条记录 last() 返回最后一条记录 1-4、返回布尔值的方法 exists() 如果QuerySet包含数据,判断表中是否有数据,有就返回True,否则返回False 1-5、返回数字的方法 count() 返回数据库中匹配查询(QuerySet)的对象数量。 例子: # all()找到UserInfo表中所有的对象 ret = UserInfo.objects.all() print(ret) # filter()找到名字是小明的那个人 ret1 = UserInfo.objects.filter(name='小明') print(ret1) # exclude()找到名字不是小明的那些人 ret1 = UserInfo.objects.exclude(name='小明') print(ret1) # values()返回的还是列表,但列表中的不是对象,而是字典 ret = UserInfo.objects.all().values('name', 'sex') print(ret) # values_list()返回的还是列表,但列表中的不是对象,而是元组 ret1 = UserInfo.objects.all().values_list('name', 'sex') print(ret1) # order_by('xx')根据xx升序排序,order_by('-xx')根据xx降序排序 ret = UserInfo.objects.all().order_by('id') ret1 = UserInfo.objects.all().order_by('-id') ret2 = UserInfo.objects.all().order_by('birthday') print(ret) print(ret1) print(ret2) # reverse() 对排序的结果进行反序 ret = UserInfo.objects.all().order_by('id').reverse() print(ret) # count()返回数量 ret = UserInfo.objects.all().count() print(ret) # first()返回第一条记录和last()返回最后一条记录 ret1 = UserInfo.objects.all().first() ret2 = UserInfo.objects.all().last() print(ret1) print(ret2) # exists()判断表中是否有数据 ret = UserInfo.objects.exists() print(ret) 2、单表查询之神奇的双下划线 # __lt:查询id值小于3的对象 ret = UserInfo.objects.filter(id__lt=3) print(ret) # __lte:查询id值小于等于3的对象 ret = UserInfo.objects.filter(id__lte=3) print(ret) # __gt:查询id值大于3的对象 ret = UserInfo.objects.filter(id__gt=3) print(ret) # __in:id是1,3,5的对象 ret = UserInfo.objects.filter(id__in=[1, 3, 5]) print(ret) # __in:id不是1,3,5的对象 ret = UserInfo.objects.exclude(id__in=[1, 3, 5]) print(ret) # __contains:找到名字包含"小"的对象,__icontains:和__contains一样,但是不区分大小写 ret = UserInfo.objects.filter(name__contains='小') print(ret) # __range:找到范围在[1, 3]之间的对象 ret = UserInfo.objects.filter(id__range=[1, 3]) print(ret) # __endswith:找到以"仔"结尾的对象 ret = UserInfo.objects.filter(name__endswith='仔') print(ret) # date字段还可以使用__year、__month等:找到10月出生的人 ret = UserInfo.objects.filter(birthday__month=10) print(ret)
# __isnull:判断这个字段是否为空
ret = UserInfo.objects.filter(name__isnull=True)
print(ret) # 双下还可以链式操作:找到出生小于2000年的 ret = UserInfo.objects.filter(birthday__year__lt=2000) print(ret)