Django 模型层

从实际应用角度提炼Django模型层的一些知识点, 供快速上手使用ヾ(•ω•`)o

0%

字段类型

Django 有丰富了字段类型, 为降低学习成本, 同时也为了维护方便, 尽量使用和数据库一致的数据类型.

数字类

  • IntegerField
  • BigIntField
  • FloatField
  • DecimalField

字符类

  • CharField

    # 在MySQL中对应的就是varchar, 所以必须指定长度
    name = models.CharField(max_length=200)
  • TextField

    遇到大的文件或图片时, 将其存放在静态资源服务器, 然后使用URI或者URL存储会更合理.

常用类

  • BooleanField

  • DateField

  • DateTimeField

  • AutoField

    没有指定主键时, 当前模型会自动将一个自增id的IntegerField做为主键.

  • ImageField
    用来做特殊约束和校验的字段, 会校验传入数据是否图片.

  • EmailField

    用来做特殊约束和校验的字段, 会校验传入数据是否邮箱地址.

  • UUIDField
    一般来说, uuid会用作当前数据表的主键.

字段选项/约束

  • primary_key
    如果为True, 将当前字段设置为主键, 默认为False.

  • null

    如果为TrueDjango将在数据库中存储空值为 NULL, 默认为 False.

  • blank
    代表空白字符"", 如果是True, 当前字段允许为空, 默认为False

    null是数据库上的设置, blank是字段验证上的设置.
    blank设置的字段不可以为NULL.

  • unique
    唯一约束设置, 默认为False

  • default
    设置字段默认值

  • 时间类型

    • auto_now
      每次保存时更新字段时间为现在, 只有调用Model.seve()时才会生效.

      update_time = models.DateTimeField(auto_now=True)
    • auto_now_add
      第一次创建对象时设置时间.

      create_time = models.DateTimeField(auto_now_add=True)

测试一下

创建应用

python manage.py startapp myblog

添加模板

class Person(models.Model):
name = models.CharField(max_length=200, primary_key=True)
email = models.EmailField(null=True)
date = models.DateField(auto_now=True)
update_time = models.DateTimeField(auto_now=True)
create_time = models.DateTimeField(auto_now_add=True)

null_test = models.CharField(max_length=200, null=True)
blank_test = models.CharField(max_length=200, blank=True)

更新同步

python manage.py makemigrations
python manage.py migrate

进入shell, 创建保存一条数据

python manage.py shell
>>> from myblog.models import Person
>>> p = Person(name='owq')
>>> p.save()

回头看看数据库

数据表重构

方法一

执行python manage.py migrate同步失败时

  • 更改migrations中的文件解决问题
    • 通过调试确认是哪个字段发生错误

方法二

数据结构发生改变时

  • 删除migrations文件然后推倒重来
    • django会在数据表django_migrations中创建同步记录, 需要删除对应的同步记录
    • 删除已经创建记录的表

Meta内嵌类

通过一个内嵌类Meta来定义元数据.
元数据又被称为中介数据, 用来描述数据的数据, 既“所有不是字段的东西”.

使用方法

from django.db import models

class Ox(models.Model):
horn_length = models.IntegerField()

class Meta:
ordering = ["horn_length"]
verbose_name_plural = "oxen"

字段选项

  • abstract
    将当前模型类转换为抽象类, 进行migrate的时候会被忽略, 也就不会对实体表进行修改.

  • app_label
    指定当前model从属于哪个已经注册的应用.
    一般为应用只编写视图层来处理业务逻辑. 模型层统一由一个models模块来管理
    比如博客的个人信息models, 很多应用都会使用到, 这时就要使用app_label来区分所属应用了.

  • db_table
    自定义表名, 不指定默认为应用名_模型名, 一般在以下情况使用.

    • 目标表已经存在时
    • 没有管理表的权限. 需要dba建表再开发时
    • 规范数据库的表名命名时
  • managed
    默认为True, 为False将不会针对当前模型创建和删除数据库表, 也就是会忽略该模型. 一般会在公司有DBA时使用.

  • ordering
    设置排序字段, 默认规则为升序.

    • 默认升序

      ordering = 'order_date'
    • -号表示降序

      ordering = ['-order_date']
      ordering = ['-pub_date', 'author']
    • ?号表示随机

      ordering = ['?field_name']
  • get_latest_by
    设置获取最后一条记录的排序规则, 和模型的实例方法latest()相关联

    # 升序排序 order_date 字段
    get_latest_by = 'order_date'

    # 降序排序 priority, 后升序排序 order_date
    get_latest_by = ['-priority', 'order_date']
  • indexes
    设置索引, 同样的, 在由DBA的情况下不需要用到.

    class Meta:
    indexes = [
    models.Index(fields=['last_name', 'first_name']),
    models.Index(fields=['first_name'], name='first_name_idx'),
    ]
  • constraints
    自定义约束

    class Meta:
    constraints = [
    # 创建一个检查约束
    # 确保年龄等于18
    models.CheckConstraint(check=models.Q(age__gte=18), name='age_gte_18'),

    # 创建一个唯一约束
    # 确保每个房间在每个日期只能被预订一次
    UniqueConstraint(field=['room', 'date'], name='unique_booking')
    ]
  • unique_together
    多字段唯一性约束(未来可能被弃用, 了解下就行)

    # 一组字段名,合起来必须是唯一的
    unique_together = ['driver', 'restaurant']

模型继承

直接继承

from django.db import models

class CommonInfo(models.Model):
name = models.CharField(max_length=100)
age = models.PositiveIntegerField()

class Meta:
abstract = True

class Student(CommonInfo):
home_group = models.CharField(max_length=5)

多重继承

默认情况下,Django 给每个模型一个自动递增的主键.
如果当前模型对象没有指定主键, 那么两张表主键会产生冲突, 为了解决多重继承产生的id冲突, 需要覆盖主键字段.

from django.db import models

class Student(models.Model):
# db_column为数据库中的字段
student_id = models.AutoField(primary_key=True, db_column="id")
name = models.CharField(max_length=200)


class Book(models.Model):
book_id = models.AutoField(primary_key=True, db_column="id")
book_name = models.CharField(max_length=200)


class BorrowRecord(Student, Book):

# 两张表主键冲突时, 要覆盖主键字段
_id = models.AutoField(primary_key=True, db_column="id")

borrow_time = models.DateTimeField(auto_now_add=True)

实例方法

  • save()
    将对象保存到数据库(或进行更新操作)

    只会进行数据库操作, 并不会进行字段验证.

  • clean_fields()
    验证模型上所有字段, 调用save()方法时不会被自动调用.

  • clean()
    自定义模型验证的方法.

  • validate_unique()
    验证模型上所有唯一约束, 和unique_together属性搭配使用.

  • full_clean()

    依次调用 Model.clean_fields()、 Model.clean() 和 Model.validate_unique()

  • latest()
    获取最后一条记录, 根据get_latest_by属性进行获取.

  • refresh_from_db()
    从数据库里刷新当前记录, 适用于数据库数据发生变动时.

  • __str__()
    用来自定义模型输出.

django.setup()

用来独立运行Django中的应用, 类似于第三方库的使用.
比如,编写一个脚本, 然后进行一些ORM操作, 同时也方便我们进行调试.

这里创建一个测试文件test.py, 使用它来进行调试学习.

import os
import django


def create_person_data():
for i in range(100):
person = Person(
name=f'name_{i}',
email=f'email_{i}@163.com',
null_test=None,
blank_test='',
)

# 运行字段校验
# 需要注意这里需要排除 null_test 字段, 忽略 NULL 值的校验
person.clean_fields(exclude=['null_test'])

# 保存数据
person.save()


if __name__ == "__main__":

# 设置DJANGO_SETTINGS_MODULE环境变量
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")
django.setup()

from myblog.models import Person

create_person_data()

QuerySet API

QuerySet 本身被构造,过滤,切片,或者复制赋值等操作,是无需访问数据库的。
只有在你需要从数据库取出数据或者向数据库存入数据时才需要访问数据库.

下面用CRUD给这些方法进行分类

  • Create

    • save()
  • Retrieve

    • all()
      获取表中所有记录.

      Model.objects.all()
    • get()
      通过指定条件, 获取一条数据, 如果有多条或不存在, 则会报错.

      # SELECT ... WHERE id = 22;
      Model.objects.get(id=22)
    • filter()
      匹配满足条件的记录, 类似于SQL中的WHERE语句.

      # SELECT ... WHERE name = name_0;
      Model.objects.filter(name="name_0")

      Django使用__来作为过滤条件标识符, 后面接上关键字来进行查询.

      条件查询

      • gt --- >
      • get -- >=
      • lt --- <
      • lte -- <
      # SELECT ... WHERE id > 22;
      Model.objects.filter(id__gt=22)

      # SELECT ... WHERE id <= 22;
      Model.objects.filter(id__lte=22)

      模糊查询

      • startswith --- 匹配开头
      • endswith ----- 匹配结尾
      • icontains ---- 匹配内容
      # SELECT ... WHERE name LIKE 'Lennon%';
      Model.objects.filter(name__startswith='Lennon')

      # SELECT ... WHERE name LIKE '%Lennon%';
      Model.objects.filter(name__startswith='%Lennon%')
    • exclude()
      匹配不匹配对象, 与filter()逻辑相反.

    • latest()

      返回最后一条数据.

    • order_by()

      设置排序, 可以覆盖ordering选项

      # 这里需要注意, 排序并不涉及数据库操作, 是在本地进行的
      Model.objects.all().order_by("name", "id")
  • Update

    • save()
  • Delete

    • delete()

      # 删除id大于
      Model.objects.filter(id>=22).delete()

高级

原始SQL

遇到复杂的场景, 使用raw()来执行SQL语句.

# Reader._meta.db_table -- 记录所在表名
Mode.objects.raw(f"select * from {Reader._meta.db_table} limit 1")

多个数据库

修改mysite/settings.py添加数据库配置.

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

使用 using 参数指定数据库.

person.save(using='person')
Person.objects.using('person').all()

事务

使用transaction.atomic()控制数据库事务

  • 基于HTTP请求的事务

    • settings中要设置ATOMIC_REQUESTSTrue
    from django.db import transaction

    @transaction.atomic
    def viewfunc(request):
    # 在事务中执行的代码
    ...
  • 基于视图具体逻辑的事务

    from django.db import transaction

    def viewfunc(request):
    ...
    with transaction.atomic():
    # 在事务中执行的代码
    ...

注意,只有视图被限制在事务中执行。中间件在事务之外运行,同理,渲染模板响应也是在事务之外运行的。

参考文档📕

模型 | Django 文档 | Django (djangoproject.com)
模型字段参考 | Django 文档 | Django (djangoproject.com)
模型 Meta 选项 | Django 文档 | Django (djangoproject.com)
约束参考 | Django 文档 | Django (djangoproject.com)
Django 配置 | Django 文档 | Django (djangoproject.com)

------------ 已触及底线了 感谢您的阅读 ------------
  • 本文作者: OWQ
  • 本文链接: https://www.owq.world/94470483/
  • 版权声明: 本站所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处( ̄︶ ̄)↗