Django学习笔记6

模型层:Object Relational Mapping(ORM)

ORM

  • 定义

ORM是Object Relational Mapping的简称,中文翻译为对象关系模型,是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术,ORM在业务逻辑层和数据库层之间充当了桥梁的作用。

  • 由来

让我们从O/R开始。字母O起源于"对象"(Object),而R则来自于"关系"(Relational)。

几乎所有的软件开发过程中都会涉及到对象和关系数据库。在用户层面和业务逻辑层面,我们是面向对象的。当对象的信息发生变化的时候,我们就需要把对象的信息保存在关系数据库中。

按照之前的方式来进行开发就会出现程序员会在自己的业务逻辑代码中夹杂很多SQL语句用来增加、读取、修改、删除相关数据,而这些代码通常都是重复的

  • 本质

每个模型都是一个Python类,它是django.db.models.Model的子类。

  • 优势

ORM解决的主要问题是对象和关系的映射,按照规定的语法写,自动翻译成对应的SQL语句.

1.不用自己写SQL语句
2. 开发效率高

  • 劣势

ORM的缺点是会在一定程度上牺牲程序的执行效率。

ORM用多了SQL语句就不会写了,关系数据库相关技能退化...

  • ORM能做的事儿:

1. 操作数据表 ===> 创建表/删除表/修改表
操作models.py里面的类
2. 操作数据行 ===> 数据的增删改查
不能创建数据库,自己动手创建数据库

Django中的ORM

Django项目如何使用ORM连接MySQL

1. 手动创建数据库
2. 在settings.py里面,配置数据库的连接信息

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'booksystem',
        'HOST': '127.0.0.1',
        'PORT': 3306,
        'USER': 'root',
        'PASSWORD': 'root',
    }
}

3. 在项目/__init__.py告诉Django用pymysql模块代替MySQLdb来连接MySQL数据库

import pymysql
pymysql.install_as_MySQLdb()

4. 在app/models.py里面定义类

# 作者类
class Author(models.Model):
    id = models.AutoField(primary_key=True)  # 自增的ID主键
    name = models.CharField(max_length=16, null=False, unique=True)
    book = models.ManyToManyField(to="Book")  # 创建作者表和书籍表多对多的关系
    # 多对多的关系会在数据库中另创建一个新的对应关系表,只存放id的对应关系

    def __str__(self):
        return "<Author object>: {}".format(self.name)

5. 执行两个命令

在哪儿执行?
在项目的根目录(有manage.py文件的那个目录)

  • python3 manage.py makemigrations --> 把models.py里面的更改记录到小本本上
  • python3 manage.py migrate --> 把更改翻译成SQL语句,去数据库执行

简单使用

下面这个例子定义了一个 Person 模型,包含 first_name 和 last_name

from django.db import models

class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

first_name 和 last_name 是模型的字段。每个字段被指定为一个类属性,每个属性映射到一个数据库列。

上面的 Person 模型将会像这样创建一个数据库表:

CREATE TABLE myapp_person (
    "id" serial NOT NULL PRIMARY KEY,
    "first_name" varchar(30) NOT NULL,
    "last_name" varchar(30) NOT NULL
);

一些说明:

  • 表myapp_person的名称是自动生成的,如果你要自定义表名,需要在model的Meta类中指定 db_table 参数,强烈建议使用小写表名,特别是使用MySQL作为后端数据库时。
  • id字段是自动添加的,如果你想要指定自定义主键,只需在其中一个字段中指定 primary_key=True 即可。如果Django发现你已经明确地设置了Field.primary_key,它将不会添加自动ID列
  • 本示例中的CREATE TABLE SQL使用PostgreSQL语法进行格式化,但值得注意的是,Django会根据配置文件中指定的数据库后端类型来生成相应的SQL语句。
  • Django支持MySQL5.5及更高版本。

字段类型

单表字段类型

  • 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

参数(上面两个都能用):

auto_now_add = True 创建数据行时(对象)就会把当前时间添加到数据库
auto_now = True 每次修改数据行(对象)时会把当前时间添加到数据库中
  • TimeField(DateTimeCheckMixin, Field)

- 时间格式 HH:MM[:ss[.uuuuuu]]

  • DurationField(Field)

- 长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型

  • FloatField(Field)

- 浮点型

  • DecimalField(Field)

- 10进制小数
- 参数:

max_digits,小数总长度
decimal_places,小数位长度
  • BinaryField(Field)

- 二进制类型

关联关系字段

Django还定义一系列字段来描述数据库之间的关联。

ForeignKey(多对一

class  ForeignKey(toon_delete**options)

多对一关系。要求两个位置参数:模型相关的类和on_delete选项。 on_delete实际上并不是必需的,但不提供它会给出已废弃的警告。 在Django 2.0中将需要它。)

若要创建递归关联关系 ——————— 一个对象与自己具有多对一关联关系 — 请使用models.ForeignKey('self', on_delete=models.CASCADE)

  • 引用关系(三种情况)

如果你需要关联到一个还没有定义的模型,你可以使用模型的名字而不用模型对象本身:

from django.db import models

class Car(models.Model):
    manufacturer = models.ForeignKey(
        'Manufacturer',
        on_delete=models.CASCADE,
    )
    # ...

class Manufacturer(models.Model):
    # ...
    pass

抽象模型上定义的这种关联关系在模型子类化为具体模型时解析,并且不相对于抽象模型的app_label

products/models.py
from django.db import models

class AbstractCar(models.Model):
    manufacturer = models.ForeignKey('Manufacturer', on_delete=models.CASCADE)

    class Meta:
        abstract = True
production/models.py
from django.db import models
from products.models import AbstractCar

class Manufacturer(models.Model):
    pass

class Car(AbstractCar):
    pass

# Car.manufacturer将指向这里的`production.Manufacturer`。

若要引用在其它应用中定义的模型,你可以用带有完整标签名的模型来显式指定。 例如,如果上面的Manufacturer模型是在一个名为production的应用中定义的,你应该这样使用它:

class Car(models.Model):
    manufacturer = models.ForeignKey(
        'production.Manufacturer',
        on_delete=models.CASCADE,
    )

这种称为懒惰关系的引用在解析两个应用程序之间的循环导入依赖关系时可能很有用。

  • 数据库表示

在幕后,Django 会在字段名上添加"_id" 来创建数据库中的列名。 在上面的例子中,Car 模型的数据库表将会拥有一个manufacturer_id 列。 (你可以通过指定db_column来显式更改)但是,除非你编写自定义SQL,否则代码不应该处理数据库列名。 你应该永远只处理你的模型对象中的字段名称。

ForeignKey 会自动创建数据库索引。 你可以通过设置db_index 为False 来取消。 如果你创建外键是为了一致性而不是用来Join,或者如果你将创建其它索引例如部分或多列索引,你也许想要避免索引的开销。

  • 参数

ForeignKey.to
  要进行关联的表名,to=“User”和to=User,to本质是和类做关联,当User类在当前模块被导入时,才可以不加双引号,否则要加双引号
ForeignKey.on_delete
   当删除由ForeignKey引用的对象时,Django将模拟由on_delete参数指定的SQL约束的行为。  自1.9版以来已弃用 on_delete将成为Django 2.0中必需的参数。 在旧版本中,默认为CASCADE。
  • CASCADE
    级联删除,删除关联数据,与之关联也删除,Django模拟SQL约束ON DELETE CASCADE的行为,并删除包含ForeignKey的对象。
  • PROTECT
    抛出 ProtectedError 以阻止被引用对象的删除,它是 django.db.IntegrityError 的一个子类。
  • SET_NULL

    删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空)

  • SET_DEFAULT
    删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值)
  • SET()
    设置 ForeignKey 为传递给 SET() 的值,如果传递的是一个可调用对象,则为调用后的结果。  在大部分情形下,传递一个可调用对象用于避免models.py 在导入时执行查询:
    a. 与之关联的值设置为指定值,设置:
    models.SET(值)
    b. 与之关联的值设置为可执行对象的返回值,设置:
    class MyModel(models.Model):
        user = models.ForeignKey(
            to="User",
            to_field="id",
            on_delete=models.SET(func), 
        )
  • DO_NOTHING

    不采取任何动作。 如果您的数据库后端强制引用完整性,除非手动添加SQL ON DELETE约束,否则将导致IntegrityError到数据库字段。

ForeignKey.limit_choices_to

当这个字段使用ModelForm或者Admin 渲染时(默认情况下,查询集中的所有对象都可以使用),为这个字段设置一个可用的选项。 它可以是一个字典、一个Q 对象或者一个返回字典或Q对象的可调用对象。

# 在Admin或ModelForm中显示关联数据时,提供的条件:
# 如:
- limit_choices_to={'nid__gt': 5}
- limit_choices_to=lambda : {'nid__gt': 5}

from django.db.models import Q
- limit_choices_to=Q(nid__gt=10)
- limit_choices_to=Q(nid=8) | Q(nid__gt=10)
- limit_choices_to=lambda : Q(Q(nid=8) | 
ForeignKey.related_name

反向操作时,使用的字段名,用于代替 【表名_set】 如: obj.表名_set.all()。它还是related_query_name 的默认值。

如果你不想让Django 创建一个反向关联,请设置related_name 为 '+' 或者以'+' 结尾。 例如,下面这行将确定User 模型将不会有到这个模型的返回关联:

user = models.ForeignKey(
    User,
    on_delete=models.CASCADE,
    related_name='+',
)
ForeignKey.related_query_name

反向操作时,使用的连接前缀,用于替换【表名】 如:

models.UserGroup.objects.filter(表名__字段名=1).values('表名__字段名')

  用于过滤器或者value,不直接用于.后面

ForeignKey.to_field

关联到的关联对象的字段名称。 默认地,Django 使用关联对象的主键。 如果引用其他字段,该字段必须具有unique=True

ForeignKey. db_constraint

控制是否在数据库中为这个外键创建约束。 默认值为True,这几乎是你想要的;将此设置为False可能对数据完整性非常不利。 即便如此,有一些场景你也许想要这么设置:

  • 你有遗留的无效数据。
  • 你正在对数据库缩容。

如果被设置成False,访问一个不存在的关联对象将抛出 DoesNotExist 异常。

ManyToManyField(多对多)

class  ManyToManyField(to**options)[source]

一个多对多关联。关联的对象可以通过字段的RelatedManager 添加、删除和创建。

数据库表示

在幕后,Django 创建一个中间表来表示多对多关系。 默认情况下,这张中间表的名称使用多对多字段的名称和包含这张表的模型的名称生成。 因为某些数据库支持的表的名字的长度有限制,这些表的名称将自动截短到64 个字符并加上一个唯一性的哈希值。 这意味着你可能会看到像author_books_9cdf4这样的表名;这是完全正常的。 你可以使用db_table选项手工提供中间表的名称。

参数

  • ManyToManyField.to
  • ManyToManyField.to_filed
  • ManyToManyField.on_delete
  • ManyToManyField.related_name
  • ManyToManyField.related_query_name

上面几个属性参考foreignerkey的参数即可相同。

  • ManyToManyField.limit_choices_to

ForeignKey.limit_choices_to 相同。

ManyToManyField 对于使用through 参数自定义中间表的limit_choices_to 不生效。

  • ManyToManyField.symmetrical

只用于与自身进行关联的ManyToManyField。 例如下面的模型:

from django.db import models

class Person(models.Model):
    friends = models.ManyToManyField("self")

当Django 处理这个模型的时候,它定义该模型具有一个与自身具有多对多关联的ManyToManyField,因此它不会向person_set 类添加Person 属性。 Django 将假定这个ManyToManyField 字段是对称的 —— 如果我是你的朋友,那么你也是我的朋友。

如果你希望与self 进行多对多关联的关系不具有对称性,可以设置symmetrical 为False。 这会强制让Django 添加一个描述器给反向的关联关系,以使得ManyToManyField 的关联关系不是对称的。

  • ManyToManyField.through    (使用自己指定的第三方表)

Django 会自动创建一个表来管理多对多关系。 不过,如果你希望手动指定中介表,可以使用through 选项来指定Django 模型来表示你想要使用的中介表。

这个选项最常见的使用场景是当你想要关联更多的数据到关联关系的时候。

如果你没有显式指定through 的模型,仍然会有一个隐式的through 模型类,你可以用它来直接访问对应的表示关联关系的数据库表。 它由三个字段来链接模型。

如果源模型和目标不同,则生成以下字段:

  • id:关系的主键。
  • <containing_model>_id:声明了ManyToManyField的模型的id
  • <other_model>_id: 被ManyToManyField所指向的模型的id

如果ManyToManyField 的源模型和目标模型相同,则生成以下字段:

  • id:关系的主键。
  • from_<model>_id:源模型实例的id
  • to_<model>_id:目标模型实例的id

这个类可以让一个给定的模型像普通的模型那样查询与之相关联的记录。

  • ManyToManyField.through_fields

只能在指定了自定义中间模型的时候使用。 Django 一般情况会自动决定使用中间模型的哪些字段来建立多对多关联。 但是,考虑如下模型:

from django.db import models

class Person(models.Model):
    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)
    inviter = models.ForeignKey(
        Person,
        on_delete=models.CASCADE,
        related_name="membership_invites",
    )
    invite_reason = models.CharField(max_length=64)

Membership两个 foreign keys指向 Person (person and inviter), 这样会导致关系不清晰,Django不知道使用哪一个外键。 在这种情况下,你必须使用through_fields 明确指定Django 应该使用哪些外键,就像上面例子一样。

through_fields 接受一个2元数组 ('field1', 'field2'), 其中 field1是指向定义了ManyToManyField的那个model的 foreign key的名字(在本例中就是group,它自己定义了M2M字段,同时也在中间模型中被ForeignKey所指向 ), and field2就是目标模型的foreign key 的名字 (person in this case).

当中间模型具有多个外键指向多对多关联关系模型中的任何一个(或两个),你必须 指定through_fields。 这通用适用于recursive relationships,当用到中间模型而有多个外键指向该模型时,或当你想显式指定Django 应该用到的两个字段时。

递归的关联关系使用的中间模型始终定义为非对称的,也就是symmetrical=False —— 所以具有源和目标的概念。 这种情况下,'field1' 将作为管理关系的源,而'field2' 作为目标。

  • ManyToManyField.db_table

为存储多对多数据而创建的表的名称。 如果没有提供,Django 将基于定义关联关系的模型和字段假设一个默认的名称。

  • ManyToManyField.db_constraint

控制中间表中的外键是否创建约束。 默认值为True,这几乎是你想要的;将此设置为False可能对数据完整性非常不利。 即便如此,有一些场景你也许想要这么设置:

  • 你有遗留的无效数据。
  • 你正在对数据库缩容。

不可以同时传递db_constraint 和 through

OneToOneField(一对一)

class  OneToOneField(toon_deleteparent_link=False**options)[source]

一对一关联关系。 概念上讲,这个字段类似ForeignKey设置了unique=True,不同的是关联关系的另一边会直接返回单个对象。选定了以后别的不能选了,不像多对一,别的还能选

什么时候用一对一?
当 一张表的某一些字段查询的比较频繁,另外一些字段查询的不是特别频繁,把不怎么常用的字段,单独拿出来做成一张表,然后用过一对一关联起来。

一对一关系使用案例

# 作者
class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    phone = models.IntegerField()
    books = models.ManyToManyField(to="Book", related_name="authors")
    detail = models.OneToOneField(to="AuthorDetail")

    def __str__(self):
        return self.name


# 作者详情
class AuthorDetail(models.Model):
    # 爱好
    hobby = models.CharField(max_length=32)
    # 地址
    addr = models.CharField(max_length=128)

  注意:一对一关系时,默认值会不好使,以为这列不能有相同的,全为none也不行

如果你没有指定OneToOneField 的related_name 参数,Django 将使用当前模型的小写的名称作为默认值。

例如下面的例子:

from django.conf import settings
from django.db import models

class MySpecialUser(models.Model):
    user = models.OneToOneField(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
    )
    supervisor = models.OneToOneField(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name='supervisor_of',
    )

你将使得User 模型具有以下属性:

>>> user = User.objects.get(pk=1)
>>> hasattr(user, 'myspecialuser')
True
>>> hasattr(user, 'supervisor_of')
True

自定义字段

class UnsignedIntegerField(models.IntegerField):

    def db_type(self, connection):

        return 'integer UNSIGNED'



PS: 返回值为字段在数据库中的属性,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)',

  自定义char类型字段:

class FixedCharField(models.Field):
    """
    自定义的char类型的字段类
    """
    def __init__(self, max_length, *args, **kwargs):
        self.max_length = max_length
        super(FixedCharField, self).__init__(max_length=max_length, *args, **kwargs)

    def db_type(self, connection):
        """
        限定生成数据库表的字段类型为char,长度为max_length指定的值
        """
        return 'char(%s)' % self.max_length


class Class(models.Model):
    id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=25)
    # 使用自定义的char类型的字段
    cname = FixedCharField(max_length=25)

  创建的表结构:

字段通用参数

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中显示选择框的内容,用不变动的数据放在内存中从而避免跨表操作
如:gf = models.IntegerField(choices=[(0, '何穗'),(1, '大表姐'),],default=1)

error_messages 自定义错误信息(字典类型),从而定制想要显示的错误信息;
字典健:null, blank, invalid, invalid_choice, unique, and unique_for_date
如:{'null': "不能为空.", 'invalid': '格式错误'}

  

猜你喜欢

转载自www.cnblogs.com/wlx97e6/p/9865172.html