Django Model模型

Model简介

模型准确且唯一的描述了数据。它包含您储存的数据的重要字段和行为。一般来说,每一个模型都映射一张数据库表。

  • 每个模型都是一个 Python 的类,这些类继承 django.db.models.Model;
  • 模型类的每个属性都相当于一个数据库的字段;
  • 利用这些,Django 提供了一个自动生成访问数据库的 API;

使用模型

在定义模型时,每个字段都被指定为一个类属性,并且每个属性映射为一个数据库列。

创建数据库时,会自动添加主键id字段,但是这种行为可以被改写。

from django.db import models

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

如果想使用自定义的模型,需要修改设置文件中的INSTALLED_APPS,在这个设置中添加包含models.py文件的模块名称。例如项目中的myapp.models模块;

INSTALLED_APPS = [
    #...
    'myapp',
    #...
]

向INSTALLED_APPS添加新的应用的时候,务必运行 manage.py migrate,也可以先使用命令manage.py makemigrations进行迁移。

字段

模型中最重要且唯一必要的是数据库的字段定义,字段在类属性中定义,并且每一个字段都应该是某个Field类的实例。

常用字段类型

AutoField():一个IntegerField,根据可用ID自动递增。如果没指定主键,就创建它自动设置为主键。

IntegerField():一个整数;

CharField(max_length = None):字符串字段

DateField(auto_now=False, auto_now_add=False):日期

TextField():一个很长的的文本字段

BooleanField():布尔字段;

OneToOneField(to, on_delete, parent_link = False):一对一

ForeignKey(to, on_delete):一对多

ManyToManyField(to):多对多

字段参数

null:如果设置为True,当该字段为空时,Django会将数据库中该字段设置为NULL。默认为False 。

blank:如果设置为True,该字段允许为空。默认为False。

default:该字段的默认值。可以是一个值或者是个可调用的对象,如果是个可调用对象,每次实例化模型时都会调用该对象。

primary_key:如果设置为 True ,将该字段设置为该模型的主键。

unique:如果设置为 True,这个字段的值必须在整个表中保持唯一。

verbose_name:任何字段类型都接收一个可选的位置参数,如果未指定Django会自动使用字段的属性名作为该参数值,并且把下划线转换为空格。

ForeignKey.on_delete:当ForeignKey删除引用的对象时,Django将模拟on_delete参数指定的SQL约束的行为 。

    CASCADE, 删除引用的对象时,也删除引用它的对象;
    PROTECT, 通过抛出异常来禁止删除引用的对象。
    SET_NULL, 将引用设置为NULL,只有在null为True的情况下。
    SET_DEFAULT, 设置默认值,ForeignKey必须设置默认值。
    SET(...), 设置给定值。
    DO_NOTHING, 不采取行动。如果数据库后端强制引用完整性,这将导致完整性错误,除非手动向数据库字段添加SQL on delete约束。
on_delete参数

自动设置主键

在一个模型中,如果你没有对任何一个字段设置primary_key=True选项。 Django会自动添加一个IntegerField字段,并设置为主键,因此除非想重写Django默认的主键设置行为,可以不手动设置主键。主键字段是只可读的,如果你修改一个模型实例的主键并保存,这等同于创建了一个新的模型实例

id = models.AutoField(primary_key=True)  #默认添加的自增主键

Meta()

使用内部Meta类来给model定义元数据;

abstract
如果abstract=True,那么model为抽象基类;

app_label
在其他地方写了一个模型类,而这个模型类是属于myapp的,那么需要指定app_label='myapp';

db_table
用于指定自定义数据库表名的;

get_latest_by
由于Django的管理方法中有个lastest()方法,就是得到最近一行记录。如果你的数据模型中有 DateField 或 DateTimeField 类型的字段,你可以通过这个选项来指定lastest()是按照哪个字段进行选取的。

managed
由于Django会自动根据模型类生成映射的数据库表,如果你不希望Django这么做,可以把managed的值设置为False。

ordering
这个字段是告诉Django模型对象返回的记录结果集是按照哪个字段排序的。加负号为反序ordering = ['-create_time']

permissions
创建此对象时进入权限表的额外权限。
指定了一个附加权限: can_deliver_pizzas
permissions = (("can_deliver_pizzas", "Can deliver pizzas"),)
这是一个2-元素 tuple 的tuple或列表, 其中两2-元素 tuple 的格式为:(permission_code, human_readable_permission_name)。

unique_together
一起使用的字段名称集必须是唯一的,这是一个列表,这些列表在一起考虑时必须是唯一的
unique_together = [['driver', 'restaurant']]

verbose_name
给model类起一个更可读的名字:

verbose_name_plural
model的复数名称
Meta属性

QuerySet API

一旦创建 数据模型 后,Django自动给予一套数据库抽象API,允许你创建、检索、更新和删除对象。

创建对象

为了用 Python 对象展示数据表对象,Django使用了一套直观的系统:一个模型类代表一张数据表,一个模型类的实例代表数据库表中的一行记录。

要创建一个实例对象,有两种创建方式:

  • 先用关键字参数初始化,然后调用 save() 将其存入数据库。
  • 直接调用create(** kwargs)方法,创建对象并将其更新至数据库;

save(force_insert = False, force_update = False, using=DEFAULT_DB_ALIAS, update_fields=None);

调用方法时,会将对象保存至数据库。如果想自定义保存行为,可以重写这个方法。

在调用save()之前无法确定实例对象的自增主键是什么,因为自增id是数据库计算的,也不是Django计算的,除非明确指定;

如果实例对象的主键属性设置为True(除None空字符串以外的值),那么django会执行UPDATE;

如果实例对象的主键属性没被设置,或者没有更新任何信息(例如主键设置了一个数据库中不存在的值),那么Django会执行INSERT;

如果实例对象执行save(),那么数据库中这条数据的所有字段都会被更新一次,无论字段是否有变化;

可以指定update_fields参数,可以是一个列表,列表中是需要更新的字段名;此时数据库只会对指定字段进行更新;

可以通过指定force_insert, force_update参数,让数据强制进行INSERT或UPDATE操作。

th = Teacher(teacher_name='Alice', sex=True)
th.save()

th = Teacher()
th.teacher_name = 'Even'
th.sex = False
th.save()
save()示例

create(**kwargs)

创建对象并将其全部保存在一个步骤中的便捷方法

#指定关键字参数
School.objects.create(school_name='清华',city='beijing')

#传递字典参数
sh_info = {'school_name': '北大', 'city': 'beijing'}
School.objects.create(**sh_info)
create()示例

删除对象

delete(using=DEFAULT_DB_ALIAS, keep_parents=False);

立刻删除对象,并返回被删除的对象数量和一个包含了每个被删除对象类型的数量的字典。也可以批量删除对象,所有的QuerySet都有delete()方法,它会删除QuerySet中的所有成员。

当Django删除某个对象时,默认会模仿SQL约束ON DELETE CASCADE的行为——换而言之,某个对象被删除时,关联对象也会被删除;这种约束行为由ForeignKey的on_delete参数指定。

# 删除某一个对象
school1.delete()

# 删除QuerySet中的所有成员
School.objects.filter(city='beijing').delete()

# 删除School的所有对象
School.objects.all().delete()
delete()示例

更新对象

update(** kwargs);

对QuerySet中指定的字段执行SQL更新查询,并返回匹配的行数(如果某些行已具有新值,则可能不等于更新的行数)。

可以同时跟新多个字段;

方法立即应用,执行后就改变了数据库的数据;

QuerySet更新的唯一限制是,只能更新模型主表中的列,不能更新相关联的模型中的列。

# 更新一个列
Student.objects.filter(pk=2).update(age=18)
# 更新QuerySet中的多个对象中的多个列
st_info = {'age': 18, 'score': 666}
Student.objects.filter(id__lt=3).update(**st_info)
# 更新与其他model相关联的列
sh = School.objects.get(pk=4)
Student.objects.filter(pk=3).update(school=sh)
update()示例

检索对象

要从数据库检索对象,就要通过模型类的Manager构建一个QuerySet;一个QuerySet代表来自数据库中对象的一个集合,可以根据给定参数缩小查询结果量。在SQL的层面上, QuerySet对应SELECT语句,而过滤器对应类似 WHERE 或 LIMIT 的限制子句。

QuerySet是惰性的,创建 QuerySet 并不会引发任何数据库活动,Django只有在QuerySet被使用时才执行查询操作。

Manager 是一种接口,它赋予了Django模型操作数据库的能力。Django应用中每个模型拥有至少一个 Manager,默认名称是objects。Managers只能通过模型类访问,而不是通过模型实例,目的是强制分离“表级”操作和“行级”操作。

#model类可以直接调用
all():返回一个包含数据表中所有对象的QuerySet;
get(**kwargs):返回一个满足给定查询参数的对象,没有则DoesNotExist;
filter(**kwargs):返回一个新的QuerySet,包含的对象满足给定查询参数;
exclude(**kwargs):返回一个新的QuerySet,包含的对象不满足给定查询参数;

#QuerySet可以调用
order_by(*fields):根据指定字段进行排序
values(*fields, **表达式):
distinct(*field):去掉重复的行
reverse():反转查询集元素的顺序;
count():返回一个整数,表示与数据库匹配的QuerySet中的对象数;
first():返回查询集匹配的第一个对象
last():返回查询集中的最后一个对象
exists():如果QuerySet包含任何结果返回True,不包含则为False;
union(*other_qs,all=False):组合其他QS的结果;
difference(*other_qs):保留调用QS中存在,其他QS不存在的元素;
QuerySet API
School.objects.all()  #, , , ]>
School.objects.get(school_name='北京大学')  #北京大学
School.objects.filter(city='beijing')  #, ]>
School.objects.exclude(city='beijing')  #, ]>
School.objects.filter(pk=1).values()  #
School.objects.values('city').distinct()  #
School.objects.order_by('pass_line')  # , , , ]>
School.objects.order_by('pass_line').reverse()#, ,,]>
School.objects.count()  #4
School.objects.filter(city='beijing').first()  #清华大学
School.objects.filter(city='beijing').last()  #北京大学
School.objects.filter(pass_line__gt=500).exists()  #True
School.objects.filter(pass_line__gt=700).exists()  #False
QuerySet API方法示例

字段查询

字段查找是指定SQL WHERE子句的内容的方式。它们被指定为QuerySet方法的关键字参数;通过给定的限制条件查找出相对应子集。

in:在给定的可迭代对象中
gt, gte, lt, lte:大于小于
range:给定对象范围内
isnull:是否为空
exact, iexact:完全符合
contains, icontains:包含匹配
startswith, istartswith:开头
endswith, iendswith:结尾
regex, iregex:正则表达式
field查询
Student.objects.filter(age__in=[18,19])  #, ]>
Student.objects.filter(age__gt=20)  #]>
Student.objects.filter(age__lt=20)  #, ]>
Student.objects.filter(score__range=[650, 700])  #]>
Student.objects.filter(age__isnull=True)  #
Student.objects.filter(student_name__exact='Ming')  #]>
Student.objects.filter(student_name__contains='e')  #, ]>
Student.objects.filter(student_name__startswith='L')  #]>
Student.objects.filter(student_name__endswith='y')  #]>
Student.objects.filter(student_name__regex='[A-Z]e[a-z]*')  #, ]>
field查询示例

聚合查询

Aggregate(*expressions, output_field = None, distinct = False, filter = None, ** extra)

聚合表达式是Func()表达式的一种特殊情况,查询需要一个聚合函数。

Avg():返回给定表达式的平均值
Count():返回给定表达式的总计数
Max():返回给定表达式的最大值
Min():返回给定表达式的最小值
Sum():计算给定表达式的所有值的总和
聚合函数
from django.db.models import Avg, Count, Max, Min, Sum

Student.objects.aggregate(Avg('score'))  #{'score__avg': 618.0}
Student.objects.aggregate(Count('score'))  #{'score__count': 3}
Student.objects.count()  #3
Student.objects.aggregate(Max('score'))  #{'score__max': 666}
Student.objects.aggregate(min_value=Min('score'))  #{'min_value': 560}
Student.objects.aggregate(sums=Sum('score'))  #{'sums': 1854}
聚合函数示例

分组查询

annotate(*args, **kwargs);

如果先使用values()方法对整体分组,聚合函数只能针对某组对象进行处理;而annotate()可以针对每组进行处理。

例如:查询每个城市的学校数量,和每个城市学校最高录取分数

School.objects.values('city').annotate(Count('pass_line')) 
#
School.objects.values('city').annotate(Max('pass_line')) 
#
分组查询示例

F查询

一个F()对象表示一个模型字段或注释的列的值。它可以引用模型字段值并使用它们执行数据库操作,而无需将它们从数据库中拉出到Python内存中。

让数据库而不是Python来做工作;

避免多线程中对数据库中字段的抢占;

减少一些操作所需的查询数量。

from django.db.models import F

school = School.objects.get(pk=3)
school.pass_line = F('pass_line') + 20
school.save()

school = School.objects.filter(pk=3)
school.update(F('pass_line') + 20)
F查询

django遇到F()示例时,它会覆盖python运算符来创建一个封装的SQL表达式,指示数据库增加由school.pass_line表示的数据库字段。

无论school.pass_line的值是什么,python都不会知道,它完全由数据库处理,此时的F()实例就像一个数据库字段的引用。

使用F()函数保存值后,再次使用实例调用并不能拿到新的值.这是因为F()函数是数据库操作,并不是在内存中python进行的;要访问保存后的新值时,必须重新加载该对象。

school = School.objects.get(pk=school.pk)
#or
school.refresh_from_db()
重新获取

保存F()实例后,赋值给模型字段的对象将会保留,每次执行save()时,字段的F()也会执行一次。

school = School.objects.get(pk=3)
school.pass_line = F('pass_line') + 20
school.save()
school.save()
持久性

如果pass_line初始值是500,最终值是540;通过在保存模型对象后重新加载模型对象,例如使用refresh_from_db()可以避免此持久性。

Q查询

封装一组字段查询操作。

在字段查询中,可以使用,分割并列的查询条件;但字段中不能进行其他关系条件的查询。

使用Q()实例字段查询后,可以进行&|~等条件的查询。

from django.db.models import F

School.objects.filter(city='beijing', pass_line__gt=680)  #]>
School.objects.filter(Q(city='beijing') & Q(pass_line__gt=680)) #]>
School.objects.filter(Q(city='beijing') | Q(pass_line__gt=680)) #, ]>
School.objects.filter(~Q(city='beijing'))  #, ]>
School.objects.filter(Q(pass_line__lt=660) & (Q(city='shandong') | Q(city='shanghai')))  #]>
Q查询

Q查询与字段查询共同使用时,字段查询需要放在Q()的后面。

School.objects.filter(Q(city='shandong') | Q(city='shanghai'), pass_line__lt=660)  #]>
con = Q()

q1 = Q()
q1.connector = 'OR'
q1.children.append(('id', 1))
q1.children.append(('id_lt', 2))
q1.children.append(('name_contains', 'hi'))

q2 = Q()
q2.connector = 'OR'
q2.children.append(('status', '在线'))

con.add(q1, 'AND')
con.add(q2, 'AND')

models.Tb1.objects.filter(con)
多条件,实例化Q()

关联对象

当你在模型中定义了关联关系(如ForeignKey,OneToOneField,或 ManyToManyField),该模型的实例将会自动获取一套 API,能快捷地访问关联对象。

一对一

表结构

OneToOneField(to, on_delete, parent_link = False, **options)

 一对一的关系。

#一个学生只能有一个学号和档案号
#一个学号和档案号只能属于一个学生
#身份信息和学生就可以建立一对一关联
from django.db import models


class Student(models.Model):
    student_name = models.CharField(max_length=20)
    age = models.IntegerField(default=15)
    identity = models.OneToOneField('Identity', on_delete=models.CASCADE)


class Identity(models.Model):
    student_number = models.IntegerField()
    archive_number = models.IntegerField()
models关系

查找

# 正向查找
st = Student.objects.get(pk=2)
print(st.identity)  #20190711011

# 反向查找
id = Identity.objects.get(pk=1)
print(id.student)  #Ming
一对一查找

一对多

表结构

ForeignKey(to, on_delete, **options)

一对多的关系。需要两个位置参数:与模型相关的类和on_delete选项。在数据库上自动创建数据库索引ForeignKey,您可以通过设置db_index为禁用此功能False。

可以通过'self'来创建递归关系,与自身具有一对多的关系。

当添加一个外键后,Django追加_id字段名来创建数据库的列名;Student的数据表中的school字段将变为school_id列。

#一个学生只能在一个学校就读
#一个学校中可以有多个学生
#学校和学生就可以建立一对多关联
from django.db import models

class Student(models.Model):
    student_name = models.CharField(max_length=20)
    age = models.IntegerField(default=15)
    school = models.ForeignKey('School', on_delete=models.CASCADE)

class School(models.Model):
    school_name = models.CharField(max_length=20)
    city = models.BooleanField(max_length=100)
models关系

创建对象

1、在创建实例对象时,关联字段显式指定一个关联对象,或者关联字段id指定一个对象id;

#通过字段属性指定关联的对象
sh_obj1 = School.objects.filter(city='上海').first()
student1_info = {
    'student_name': '小海',
    'age': 21,
    'score': 662,
    'school': sh_obj1
}
Student.objects.create(**student1_info)

#通过指定表列名指定关联对象的id值,也可以是个数字
sh_obj2 = School.objects.filter(pass_line__lt=660).order_by('pass_line').last()
student2_info = {
    'student_name': '大B',
    'age': 20,
    'score': 672,
    'school_id': sh_obj2.id
}
Student.objects.create(**student2_info)
正向创建方式

2、也可以通过关联对象反向创建一个对象实例。

sh = School.objects.get(pk=1)
sh.student_set.create(
    student_name='大黄',
    age=23,
    score=581,
)
反向创建

正向查找

通过双下划线直接调用关联对象中的字段。

obj = Student.objects.filter(school__city='上海')
print(obj)  #]>

#连表查询,查询每个城市的学生人数
obj = Student.objects.values('school__city').annotate(Count('age'))
print(obj)  #
正向查找

模型的实例能通过其属性方便的访问关联(外部的)对象。

#通过属性调用关联对象
student = Student.objects.filter(student_name='小海').first()
print(student.school)  #上海交通大学

#可以对外键进行修改,通过save()保存至数据库
obj = School.objects.filter(city='济南').first()
student.school = obj
student.save()
student.refresh_from_db()
print(student.school)  #山东大学

# 如果ForeignKey字段配置了null=True,可以指定值为 None 移除关联
student.school = None
student.save()
student.refresh_from_db()
print(student.school)  #None
属性查找

反向查找

反向查找,可以通过外键类名的小写,加上_set,返回一个QuerySet。

sh_obj = School.objects.filter(city='济南').last()
st_obj = sh_obj.student_set.all()
print(st_obj)  #, ]>
#返回的QuerySet也可以调用筛选、排序等API
obj = st_obj.order_by('age')
print(obj)  #, ]>
反向查找

模型的实例能通过其属性方便的访问关联(外部的)对象。

#查找哪些学校中有年龄小于18的学生
obj = School.objects.filter(student__age__lt=18)
print(obj)  #]>
属性查找

add(*objs, bulk = True, through_defaults = None)

将指定的模型对象添加到相关的对象集。

st = Student.objects.get(student_name='小海')
print(st.school)  #上海交通大学
sh = School.objects.get(pk=4)
sh.student_set.add(st)
print(st.school)  #复旦大学
add()示例

在ForeignKey关系的情况下,使用QuerySet.update()更新至数据库,也可以使用bulk=False参数调用QuerySet.save()

add()也接受关联指向的字段作为参数,可以改写为sh.student_set.add(4)

remove(*objs, bulk=True)

从相关对象集中删除指定的模型对象;

st = Student.objects.get(student_name='小海')
print(st.school)  # 复旦大学
sh = School.objects.get(school_name='复旦大学')
sh.student_set.remove(st)
st.refresh_from_db()  #重新获取实例对象
print(st.school)  #None
remove()示例

在ForeignKey关系的情况下,此方法仅在null=True情况下存在;

默认使用QuerySet.update()更新至数据库,也可以使用bulk=False参数调用QuerySet.save()

remove()也接受关联指向的字段作为参数,可以改写为sh.student_set.remove(4)

clear(bulk=True)

从相关对象集中删除所有对象;

sh = School.objects.get(school_name='复旦大学')
sh.student_set.clear()
clear()示例

这不会删除相关对象,只是将关联关系取消。

只在ForeignKeys字段null=True时才可以用,它也接受bulk关键字参数。

set(objs, bulk=True, clear=False, through_defaults=None)

替换相关对象集;

new_list = [obj1, obj2, obj3]
obj.related_set.set(new_list)
set()示例

如果clear为False(默认),将利用remove()删除新集中缺少的元素,并且仅添加新元素;如果clear=True,则调用clear()方法,并立即添加整个集合。

对于ForeignKey对象,bulk 参数传递给add()和remove()的。

由于set()是复合操作,因此受竞争条件的限制。

多对多

表结构

ManyToManyField(to, **options)

多对多的关系。需要一个位置参数:与模型相关的类。

1、Django创建了一个中间数据表来建立多对多关系,默认情况下,此表名称是使用多对多字段的名称以及包含它的模型的表名生成的。

对于多对多关联关系的两个模型,可以在任何一个模型中添加ManyToManyField字段,但只能选择一个模型设置该字段,不能同时在两模型中添加该字段。

#一个学生可以有多个任课老师,
#一个老师也可以教多个学生,
#学生和老师就可以建立多对多关联class Student(models.Model):
    student_name = models.CharField(max_length=20)
    age = models.IntegerField(default=15)
    teachers = models.ManyToManyField('Teacher')

class Teacher(models.Model):
    teacher_name = models.CharField(max_length=20)
    sex = models.BooleanField(default=True)
models关系

2、在指定ManyToManyField字段时,通过through参数可以指定自定义的关系表,也就可以自定义关系表的表结构;

class Student(models.Model):
    student_name = models.CharField(max_length=20)
    age = models.IntegerField(default=15)
    score = models.IntegerField(default=560)
    teacher = models.ManyToManyField('Teacher', through="StudentToTeacher")

class Teacher(models.Model):
    teacher_name = models.CharField(max_length=20)
    sex = models.BooleanField(default=True)
    course = models.CharField(max_length=20, default='语文')

class StudentToTeacher(models.Model):
    st_id = models.AutoField(primary_key=True)
    student_id = models.ForeignKey('Student', on_delete=models.CASCADE)
    teacher_id = models.ForeignKey('Teacher', on_delete=models.CASCADE)

    class Meta:
        unique_together = [
            ('student_id', 'student_id')  #两个字段联合唯一
        ]
ManyToManyField()

3、在中间的关系表中,可以利用两个ForeignKey字段,分别指向两个关联对象,让他们在这张表中同时建立外键关系。

class Student(models.Model):
    student_name = models.CharField(max_length=20)
    age = models.IntegerField(default=15)
    score = models.IntegerField(default=560)

class Teacher(models.Model):
    teacher_name = models.CharField(max_length=20)
    sex = models.BooleanField(default=True)
    course = models.CharField(max_length=20, default='语文')
    
class StudentToTeacher(models.Model):
    st_id = models.AutoField(primary_key=True)
    student_id = models.ForeignKey('Student', on_delete=models.CASCADE)
    teacher_id = models.ForeignKey('Teacher', on_delete=models.CASCADE)
自定义关系表

创建对象

多对多不可以在创建时直接指定关联对象;要先创建对象,然后再添加关联关系。

teacher_info = {
    'teacher_name': '鲁迅',
    'sex': False,
    'course': '语文'
}
Teacher.objects.create(**teacher_info)

student_info = {
    'student_name': '老王头',
    'age': 25,
    'score': 562,
}
Student.objects.create(**student_info)
创建对象

add(*objs, through_defaults = None)

将指定的模型对象添加到相关的对象集。

多对多关系不会调用save()方法(bulk参数不存在),而是使用QuerySet.bulk_create()创建关系

ManyToManyFeild()定义Student类中,那么通过Student对象添加Teacher对象,就被视为正向添加;反之为反向添加。反向调用时通过外键class名的小写,加上_set。

#正向添加
th = Teacher.objects.filter(course='语文').first()
st = Student.objects.filter(student_name='老王头').first()
st.teacher.add(th)
print(st.teacher.all())  #]>

#反向添加
th = Teacher.objects.get(pk=1)
st = Student.objects.filter(pk__lt=3)
th.student_set.add(*st)
print(th.student_set.all())  #, ]>
add()示例

remove(*objs)

从相关对象集中删除指定的模型对象;

clear()

从相关对象集中删除所有对象;

set(objs, clear=False, through_defaults=None)

替换相关对象集;

多对多关联中的add(),set()和remove()放法能接收主键值,bulk关键字参数不存在。

转载于:https://www.cnblogs.com/houyongchong/p/model.html

你可能感兴趣的:(python,数据库,后端)