Django进阶
字段类型
有限使用, 尽量使用和数据库一致的数据类型
-
BooleanField
代表true或者false, 也就是代表1和0;
-
IntegerField
-
BigIntField
-
FloatField
-
DecimalField
-
CharField
在mysql中对应的就是varchar, 所以必须指定长度 name = models.CharField(max_length=200)
-
TextField
遇到大的文本和图片, 文件. 推荐做法是存当前的URI或者URL, 然后通过路径从静态资源服务器, 文档数据库或者云服务器中获取该文件
-
DateField
-
DateTimeField
-
AutoField
如果没有指定主键, 当前模型会自动将一个自增id的
IntegerField
做为主键. -
ImageField
-
EmailField
当前django提供用来做特殊约束和校验的字段.
-
UUIDField
一般来说, uuid会用作当前数据表的主键
字段选项/约束
-
primary_key
将当前字段设置为主键
id = models.AutoField(max_length=191, primary_key=True)
-
null
默认为False, 如果为True, 当前字段可以为NULL.
-
blank
代表空白字符
""
, 默认为False, 如果是True, 当前字段允许为空password = models.CharField(max_length=200, blank=False)
-
unique
默认为False, 如果为True, 重复记录会报错
-
default
默认值
-
时间类型字段
-
auto_now_add
create_time = models.DateTimeField(auto_now_add=True)
-
auto_now
update_time = models.DateTimeField(auto_now=True)
-
创建测试应用myblog
class Person(models.Model):
name = models.CharField(max_length=200, primary_key=True)
email = models.EmailField()
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)
调试
-
修改
manage.py
进行debug调试print(sys.argv) # sys.argv = ['manage.py', 'migrate'] execute_from_command_line(sys.argv)
-
修复
migrations
执行
python manage.py migrate
命令其实是按顺序执行未同步的migrations.- 更改
migrations
中的文件解决问题 - 删除
migrations
文件推倒重来django
会在数据表django_migrations
中创建同步记录- 删除对应的同步记录
- 如果有创建表的记录, 需要删除表
- 更改
内嵌类Meta
通过一个内嵌类Meta来定义元数据, 元数据又被称为中介数据, 用于描述数据得数据.
简单地讲, Meta就是通过属性来描述model对象, 为其提供额外得功能.
-
abstract
将当前模型类转换成抽象类, 当进行migrate的时候会被忽略.
因为migrate是对实体表进行修改, 而抽象是一个虚的概念, 所以不存在实体.
-
app_label
指定当前model从属于哪个已经注册的应用
使用场景一般为应用只编写视图层来处理业务逻辑. 模型层统一由一个models模块来管理, 所以要区分所属应用
-
db_table
自定义表名, 如果不指定表名则默认为
应用名_模型名
第一种使用情况为目标表已经存在.
第二种为开发没有管理表的权限, 需要dba建表再开发.
第三种为规范数据库的表名命名, 而不是使用django默认的命名规范.
-
managed
默认为True, 如果为False, 当前migrate会忽略该模型.
- 虽然设置了managed为False, 但是如果没有主键的化, 依然会自动创建主键.
很多公司都会由专人DBA来管理数据库.
独立开发流程: 创建模型-> migrate
公司合作开发流程: dba建表->创建模型
-
ordering
设置排序字段, 默认排序规则为升序
-
-
表示降序ordering = ['-order_date'] ordering = ['-pub_date', 'author']
-
?
表示随机的意思ordering = ['?']
-
-
get_lastest_by
设置获取最后一条记录的排序规则
-
indexes
设置索引
class Meta: indexes = [ models.Index(fields=['last_name', 'first_name']), models.Index(fields=['first_name'], name='first_name_idx'), ]
-
unique_together
多字段唯一性约束
unique_together = ("driver", "restaurant")
模型继承
-
普通继承
class Person(models.Model): name = models.CharField(max_length=191) email = models.EmailField() 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) class Meta: abstract = True class Reader(Person): whether_vip = models.BooleanField(default=False)
-
多重继承
如果当前模型对象没有指定主键, 那么django会自动帮我们创建名为id的主键字段.
为了解决多重继承产生的id冲突, 你需要覆盖id字段.
from django.db import models # Create your models here. 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)
实例方法
-
获取最新记录
get_lastest_by = ['id'] item = model.objects.latest()
-
自定义输出
def __str__(self): return f"id:{self.id}, {self.question_text}"
-
刷新当前记录
refresh_from_db
-
字段验证
比如EmailField调用clean_fields会验证当前格式是否正确
-
字段唯一性验证
# 在meta中设置唯一性字段 unique_together = ['name', 'email'] reader.validate_unique(exclude=['null_test', 'create_time', 'update_time', 'id'])
django.setup()
import os
import random
import django
def create_reader():
for i in range(10):
reader = Reader(
name=f'name_{i}',
email=f'email_{i}@163.com',
null_test=None,
blank_test='',
whether_vip=random.choice([True, False])
)
# 通过clean_fields方法对当前对象进行自检
# 如果当前字段可以为None, 则需要将该字段置入exclude参数中
reader.clean_fields(exclude=['null_test'])
# 调用save会往数据插入一条数据或者更新一条数据
reader.save()
if __name__ == "__main__":
# 设置环境变量
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")
django.setup()
from myblog.models import Reader
create_reader()
QuerySet
-
Create
- save
-
Retrieve
Django的
orm
时一条执行链, 而不是单个语句Reader.objects.all().filter(id__gt=300).order_by("name", "id")
orm尽量使用一些简单的接口
如果查询需求比较复杂, 尽量使用SQL
-
all
获取表中所有记录
如果表非常大, 一定要小心使用.
-
get
通过指定条件, 获取一条数据, 如果有多条, 则会报错
reader = Reader.objects.get(name="name_0")
-
filter
匹配满足条件的记录, 类似于
SQL
中的WHERE
语句reader_array = Reader.objects.filter(name="name_0")
-
比较
Reader.objects.filter(id__gte=300)
>=
-----gte
>
--------gt
<=
-------lte
<
--------lt
-
模糊匹配
类似于
WHERE 字段 LIKE '%xx%'
__startswith
__endswith
__icontains
-
-
exlude
与
filter
逻辑取反 -
限制返回的记录数
相当于
SQL
中的limit
reader_array[:5]
-
latest
# 需要在models, Meta中指定get_latest_by Reader.objects.latest()
-
order_by
Reader.objects.all().order_by("name", "id")
-
-
Update
-
save
reader.email = "test@163.com" reader.save()
-
-
Delete
如果想对整张表进行清除, 还是truncate table命令更快
-
delete
reader.delete()
-
高级
-
RAW
遇到复杂的场景, 需要使用
SQL
语句通过Reader._meta.db_table来获取记录所在表
queryset = Reader.objects.raw(f"select * from {Reader._meta.db_table} limit 1") list(queryset)
-
指定数据库
-
在settings配置该数据库,
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'polls', 'USER': 'root', 'PASSWORD': 'qwe369', 'HOST': '127.0.0.1', 'PORT': '3306' }, 'person': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'person', 'USER': 'root', 'PASSWORD': 'qwe369', 'HOST': '127.0.0.1', 'PORT': '3306' } }
-
使用参数using
reader.save(using='person')
Reader.objects.using('person').all()
-
-
事务
-
基于HTTP请求的事务
- 在
settings
中要设置ATOMIC_REQUESTS
为True
from django.db import transaction @transaction.atomic def my_view(request): 表达式...
- 在
-
基于视图具体逻辑的事务
from django.db import transaction def viewfunc(request): 表达式 with transaction.atomic(): 表达式A 表达式B ...
-