Django框架-05模型层相关(ORM操做)

测试ORM操做

单独的py文件测试ORM操做须要配置的参数python

import os


    if __name__ == "__main__":
        os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day58.settings")
        import django
        django.setup()
        from app01 import models  # 这一句话必须在这下面导入

查看orm内部sql语句

"""
            查看orm内部sql语句的方法有哪些
                1.若是是queryset对象 那么能够点query直接查看该queryset的内部sql语句
                2.在django项目的配置文件中 配置一下参数便可实现全部的orm在查询的时候自动打印对应的sql语句
                LOGGING = {
                    'version': 1,
                    'disable_existing_loggers': False,
                    'handlers': {
                        'console':{
                            'level':'DEBUG',
                            'class':'logging.StreamHandler',
                        },
                    },
                    'loggers': {
                        'django.db.backends': {
                            'handlers': ['console'],
                            'propagate': True,
                            'level':'DEBUG',
                        },
                    }
                }
            """

Model的表字段的增删:

增:

​ 当一张表已经建立出来以后 后续还想添加字段,能够有两种方式mysql

  1. 给新增的字段设置默认值
    addr = models.CharField(max_length=32,default='China') # default该字段默认值
  2. 给新增的字段设置成能够为空
    age = models.IntegerField(null=True) # 该字段容许为空

删(慎用):

删除字段 直接在models.py中注释该字段 而后从新执行两条命令便可
注意: 执行完以后 表中该字段所对应的全部的数据所有删除
而且通常状况下 基本是不会用到真正意义上的删除sql

数据的增删改查

查:数据库

  1. ​ models.User.objects.all() # 直接拿全部的数据
  2. ​ models.User.objects.get(username=username)
  3. ​ res = models.User.objects.filter(username=username)
    ​ res.query
    ​ user_obj = res.first()

增:django

  1. models.User.objects.create(username=username,password=password)
  2. user_obj = models.User(username=username,password=password)
    user_obj.save()

删:
models.User.objects.filter(条件).delete()并发

改:
models.User.objects.filter(条件).update()app

操做方法(13):

  1. all(): 查询全部结果
  2. filter(kwargs): 它包含了与所给筛选条件相匹配的对象**
  3. get(kwargs): 返回与所给筛选条件相匹配的对象,返回结果有且只有一个,若是符合筛选条件的对象超过一个或者没有都会抛出错误。(源码就去搂一眼~诠释为什么只能是一个对象
  4. exclude(kwargs): 它包含了与所给筛选条件不匹配的对象*
  5. **order_by(*field)**: 对查询结果排序('-id')/('price')*
  6. reverse(): 对查询结果反向排序 >>>前面要先有排序才能反向
  7. count(): 返回数据库中匹配查询(QuerySet)的对象数量。
  8. first(): 返回第一条记录
  9. last(): 返回最后一条记录
  10. exists(): 若是QuerySet包含数据,就返回True,不然返回False
  11. **values(*field)**: 返回一个ValueQuerySet——一个特殊的QuerySet,运行后获得的并非一系列 model的实例化对象,而是一个可迭代的字典序列*
  12. **values_list(*field)**: 它与values()很是类似,它返回的是一个元组序列,
  13. values: 返回的是一个字典序列
  14. distinct(): 从返回结果中剔除重复纪录

总结:

返回QuerySet对象的方法有

  • all()
  • filter()
  • exclude()
  • order_by()
  • reverse()
  • distinct()

特殊的QuerySet

  • values() 返回一个可迭代的字典序列
  • values_list() 返回一个可迭代的元祖序列

返回具体对象的

  • get() 不推荐用, 推荐filter以后配合first 获取相关对象
  • first()
  • last()

返回布尔值的方法有:

  • exists()

返回数字的方法有

  • count()

神奇的双下划线

相关方法

__gt    大于
__lt    小于
__gte   大于等于
__lte   小于等于
__in    在某范围内(包含两头)
__range     是否在设定的几个选择中

单表查询

案例:测试

"""神奇的双下滑查询"""
# 查询价格大于200的书籍
# res = models.Book.objects.filter(price__gt=200)
# print(res)
# 查询价格小于200的书籍
# res = models.Book.objects.filter(price__lt=200)
# print(res)

# 查询价格大于等于200.22的书籍
# res = models.Book.objects.filter(price__gte=200.22)
# print(res)
# 查询价格小于等于200.22的书籍
# res = models.Book.objects.filter(price__lte=200.22)
# print(res)


# 查询价格要么是200,要么是300,要么是666.66
# res = models.Book.objects.filter(price__in=[200,300,666.66])
# print(res)
# 查询价格在200到800之间的
# res = models.Book.objects.filter(price__range=(200,800))  # 两边都包含
# print(res)

# 查询书籍名字中包含p的
"""原生sql语句 模糊匹配
like 
%
_
"""
# res = models.Book.objects.filter(title__contains='p')  # 仅仅只能拿小写p
# res = models.Book.objects.filter(title__icontains='p')  # 忽略大小写

# print(res)


# 查询书籍是以三开头的
# res = models.Book.objects.filter(title__startswith='三')
# res1 = models.Book.objects.filter(title__endswith='p')
# print(res)
# print(res1)


# 查询出版日期是2017的年(******)
res = models.Book.objects.filter(create_time__year='2017')
print(res)

我的小总结: 熟练利用 __ ,熟练使用 __ 的相关方法便可fetch

多表查询

(ForeignKey操做)

表与表之间的关系

  1. 一对一(OneToOneField): 一对一字段不管建在哪张关系表里面均可以,可是推荐建在查询频率比较高的那张表里面
  2. 一对多(ForeignKey): 一对多字段建在多的那一方
  3. 多对多(ManyToManyField): 多对多字段不管建在哪张关系表里面均可以,可是推荐建在查询频率比较高的那张表里面

方法

  • add()      # 添加
  • set()      # 修改
  • remove()      # 不能接收可迭代对象
  • clear()      # 清空 不用传参

一对多经典案例:

# 一对多的字段的增删改查
# 增
# publish_id传数字
# models.Book.objects.create(title='三国演义',price=189.99,publish_id=1)
#  publish直接传出版社对象
# publish_obj = models.Publish.objects.filter(pk=2).first()
# models.Book.objects.create(title='红楼梦',price=999.99,publish=publish_obj)

# 改
# 传数字的
# models.Book.objects.filter(pk=1).update(publish_id=3)
# 传对象的
# publish_obj = models.Publish.objects.filter(pk=2).first()
# models.Book.objects.filter(pk=1).update(publish=publish_obj)

# 查

# 删
# models.Publish.objects.filter(pk=2).delete()  # 默认都是级联更新 级联删除

我的小总结:优化

正反向跨表就完事了,利用好双下滑线,上面例子没用,有些注意点就是 关于创建关联时候单表 __id对应的自动建表时候出来的字段, 给它赋对应数字id能够创建关系,要么就是 外键= 对象方式

多对多经典案例:

# 多对多字段的增删改查
# 增
# 要给主键为1的书籍添加两个做者
# book_obj = models.Book.objects.filter(pk=1).first()
# print(book_obj.authors)  # 对象点击多对多虚拟字段 会直接跨到多对多的第三张表
# book_obj.authors.add(1)
# book_obj.authors.add(2,3)

# author_obj = models.Author.objects.filter(pk=1).first()
# author_obj1 = models.Author.objects.filter(pk=2).first()
# author_obj2 = models.Author.objects.filter(pk=3).first()
# book_obj.authors.add(author_obj)
# book_obj.authors.add(author_obj1,author_obj2)
"""
add()
是给书籍添加做者  括号内既能够传数字也能够传对象
而且支持一次性传多个  逗号隔开便可
"""

# 改
# 将主键为1的书籍对象 做者修改成2,3
# book_obj = models.Book.objects.filter(pk=1).first()
# book_obj.authors.set([2,])
# book_obj.authors.set([2,3])

# author_obj = models.Author.objects.filter(pk=1).first()
# author_obj1 = models.Author.objects.filter(pk=2).first()
# author_obj2 = models.Author.objects.filter(pk=3).first()
# book_obj.authors.set([author_obj,])
# book_obj.authors.set([author_obj, author_obj1, author_obj2])
"""
set()括号内 须要传一个可迭代对象 
可迭代对象中 能够是多个数字组合
也能够是多个对象组合
可是不要混着用!!!
"""

# 删
# book_obj = models.Book.objects.filter(pk=1).first()
# # book_obj.authors.remove(3)
# book_obj.authors.remove(1,2)
# author_obj = models.Author.objects.filter(pk=1).first()
# author_obj1 = models.Author.objects.filter(pk=2).first()
# author_obj2 = models.Author.objects.filter(pk=3).first()
# book_obj.authors.remove(author_obj)
# book_obj.authors.remove(author_obj1,author_obj2)
"""
remove()括号内既能够传数字 也能够传对象 
而且支持传对个 逗号隔开便可
"""

# 将某本书跟做者的关系所有清空
# book_obj = models.Book.objects.filter(pk=1).first()
# book_obj.authors.clear()  # 清空当前书籍与做者的全部关系

"""
add()
set()
remove()
上面三个都支持传数字 或者对象 而且能够传多个 可是set须要传可迭代对象

clear()
clear括号内不须要传任何参数
"""

我的小总结:

利用好外键跨表,正反向跨表就完事了,利用好双下滑线,上面例子没用,有些注意点 ==>

  • add,set对象和id都不能混着用

  • 能够经过id或对象添加关联,支持 逗号(,) 隔开传多个

  • set()括号内 须要传一个可迭代对象 ,可迭代对象中 能够是多个数字组合

  • remove()括号内既能够传数字 也能够传对象 ,而且支持传多个 逗号隔开便可

  • clear括号内不须要传任何参数

    换句话说 只有set须要可迭代对象

跨表查询经典案例:

"""
正向与反向的概念

# 一对一
# 正向:author---关联字段在author表里--->authordetail     按字段
# 反向:authordetail---关联字段在author表里--->author     按表名小写


# 一对多
# 正向:book---关联字段在book表里--->publish      按字段
# 反向:publish---关联字段在book表里--->book      按表名小写_set.all() 由于一个出版社对应着多个图书

# 多对多
# 正向:book---关联字段在book表里--->author       按字段
# 反向:author---关联字段在book表里--->book       按表名小写_set.all() 由于一个做者对应着多个图书


正向查询按外键字段
反向查询按表名小写
"""
"""基于对象的跨表查询(子查询:将一张表的查询结果当作另一个查询语句的条件)"""
"""
强调:在书写orm语句的时候 跟写sql语句同样 
不要尝试着 一次性写完  应该作到写一点看一点再一点
"""
# 1.查询书籍id是1 的出版社名称
# book_obj = models.Book.objects.filter(pk=1).first()
# print(book_obj.publish.name)
# print(book_obj.publish.addr)

# 2.查询书籍id是2 的做者姓名
# book_obj = models.Book.objects.filter(pk=2).first()
# print(book_obj.authors)  # app01.Author.None
# print(book_obj.authors.all())
# res = book_obj.authors.all()
# for r in res:
#     print(r.name)

# 3.查询做者是jason的家庭住址
# author_obj = models.Author.objects.filter(name='jason').first()
# print(author_obj.author_detail.addr)


# 4.查询出版社是东方出版社出版的书籍
# publish_obj = models.Publish.objects.filter(name='东方出版社').first()
# # print(publish_obj.book_set)  # app01.Book.None
# print(publish_obj.book_set.all())

# 5.查询做者是jason的写过的全部的书籍
# author_obj = models.Author.objects.filter(name='jason').first()
# print(author_obj.book_set)  # app01.Book.None
# print(author_obj.book_set.all())

# 6.查询电话号码是130的做者姓名
# author_detail_obj = models.AuthorDetail.objects.filter(phone=130).first()
# print(author_detail_obj.author.name)
# print(author_detail_obj.author.age)
"""
当你反向查询的结果是多个的时候 就须要加_set
不然直接代表小写便可
"""

# 7.查询书籍id为1 的做者的电话号码
# book_obj = models.Book.objects.filter(pk=1).first()
# author_list = book_obj.authors.all()
# for author_obj in author_list:
#     print(author_obj.author_detail.phone)

"""基于双下划綫的跨表查询(连表操做)
left join
inner join
right join
union
"""
# 正向
# 1.查询jason做者的手机号
# res = models.Author.objects.filter(name='jason').values('author_detail__phone','author_detail__addr')
# print(res)

# res1 = models.AuthorDetail.objects.filter(author__name='jason').values('phone')
# print(res1)


# 查询jason这个做者的年龄和手机号
# 正向
# res = models.Author.objects.filter(name='jason').values('age','author_detail__phone')
# print(res)
# 反向
# res1 = models.AuthorDetail.objects.filter(author__name='jason').values('phone','author__age')
# print(res1)


# 查询手机号是130的做者年龄
# 正向
# res = models.AuthorDetail.objects.filter(phone=130).values('author__age')
# print(res)
# # 反向
# res1 = models.Author.objects.filter(author_detail__phone=130).values('age')
# print(res1)

# 查询书籍id是1 的做者的电话号码
# res = models.Book.objects.filter(pk=1).values('authors__author_detail__phone')
# res1 = models.Book.objects.filter(pk=1).values('外键字段1__外键字段2__外键字段3__普通字段')
# print(res)
"""只要表里面有外键字段 你就能够无限制跨多张表"""

# 1.查询出版社为北方出版社的全部图书的名字和价格
# res = models.Publish.objects.filter(name='北方出版社').values('book__title','book__price')
# print(res)

# 2.查询北方出版社出版的价格大于19的书
# res = models.Book.objects.filter(price__gt=19,publish__name='北方出版社').values('title','publish__name')
# print(res)

我的总结注意点:

  • 正向查询按字段,反向查询按表名小写
  • 当你反向查询的结果是多个的时候 就须要加_set, 不然(一对一查询场景)直接代表小写便可
  • 跨表查询全部结果 表名_set显示为 app01.Book.None, 须要再加.all()获取
  • 正向查询时候也会 出现对象.外键名 出现 app01.Book.None ,也须要 .all()获取
  • 换句话当现实 app01.Book.None 相似的 都须要 .all() 来获取
  • 双下划线查询就对应sql语句的连表查询(inner join, left join等等), 而找到对象,而后对象查询的(多句语句实现)方式对应sql语句的子查询
  • 只要表里面有外键字段 你就能够无限制跨多张表,用filter实现,filter查空queryset时候,只要没参数,依然会是空queryset,不会报错

聚合查询

aggregate()

from django.db.models import Max, Min, Count, Avg, Sum

是QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。

from django.db.models import Max,Min,Count,Sum,Avg

经典例子:

# 1.统计全部书的总价格
# from django.db.models import Max,Min,Count,Avg,Sum
#
# # res = models.Book.objects.aggregate(Sum('price'))
# res1 = models.Book.objects.aggregate(Avg('price'))
# res2 = models.Book.objects.aggregate(Count('price'))
# res3 = models.Book.objects.aggregate(Max('price'))
# res4 = models.Book.objects.aggregate(Min('price'))
# res5 = models.Book.objects.aggregate(Max('price'),Min('price'),Count('pk'),Avg('price'),Sum('price'))
# print(res5)
# print(res)
# print(res1)
# print(res2)

我的口水总结:

思考方式==> aggregate以后 对应的字符串就是搜索条件,已经获取相关的对应条件全部对象的对应条件数值后,对它进行 其余的方法,好比 (Count,Max等等) 这时候能够 = 方式赋值, 当前指向 改位置的变量 , .values() 里面能够作为字符串方式呈现, 做为搜索结果的一个 key

运用场景联想==>

针对全部时候考虑

聚合, 把分散的结合汇总起来统计 , 好比 书不少, 价格档次不一样, 这时候aggregate 根据不一样的书得到对应全部的档次(这边是全考虑,不考虑任何分组), 而后能够针对这全部档次的数据能够进一步进行方法操做

分组查询

annotate()

from django.db.models import Max, Min, Count, Avg, Sum

经典例子:

# 统计每一本书的做者个数
from django.db.models import Max, Min, Count, Avg, Sum
# res = models.Book.objects.annotate(author_num = Count('authors')).values('author_num','title')
# print(res)

# 统计出每一个出版社卖的最便宜的书的价格
# res = models.Publish.objects.annotate(mmp = Min('book__price')).values('name','mmp')
# print(res)

# 统计不止一个做者的图书
# res = models.Book.objects.annotate(author_num=Count('authors')).filter(author_num__gt=1)
# print(res)

"""
只要是queryset对象 就能够无限制的调用queryset对象的方法!!!
最最经常使用的就是对一个已经filter过滤完的数据 再进行更细化的筛选

"""

# 查询各个做者出的书的总价格
# res = models.Author.objects.annotate(sp=Sum('book__price')).values('name','sp')
# print(res)


#F查询的本质就是从数据库中获取某个字段的值
# 查询库存数大于卖出数的书籍
"""以前查询等号后面的条件都是咱们认为输入的
如今变成了须要从数据库中获取数据放在等号后面
"""

我的口水总结:

思考方式==> annotate以后 对应的字符串就是搜索条件,而后搜索条件获取对应条件的对象而后对他们进行分组,以后能够对它们进行 其余的方法,好比 (Count,Max等等) 这时候能够 = 方式赋值, 当前指向 该位置的变量 , .values() 里面能够作为字符串方式呈现, 做为搜索结果的一个 key

运用场景联想==>

针对各个状况考虑

分组, 把分散的结合汇总起来 , 好比 书不少, 价格档次不一样, 这时候annotate根据不一样的价格分红对应的档次, 每一个档次对应多个对象, 而后在每一个分组对对应的数据能够进一步进行操做, 汇总

F,Q查询使用场景联想

当搜索条件后面的参数不为固定值时候想到F或Q查询

F查询

F 能够帮咱们取到表中某个字段对应的值来看成个人筛选条件

from django.db.models import F

经典案例:

从数据库中获取字段对应的数据
列子如: 库存数大于卖出数

# res = models.Book.objects.filter(kucun__gt=F('maichu'))
# print(res)


# 将书籍库存数所有增长1000
# models.Book.objects.update(kucun=F('kucun')+1000)

# 把全部书名后面加上'新款'

# from django.db.models.functions import Concat
# from django.db.models import Value
#
# ret3 = models.Book.objects.update(title=Concat(F('title'), Value('新款')))
# models.Book.objects.update(title = F('title')+'新款')  # 不能这么写

我的总结:

当搜索条件后面的参数不为固定值时候想到F,须要字符串拼接时候,用 Concat, Value, 不过须要导入,直接弄 会出问题,全部字段都为数据单一

Q查询

Q 实现取反和或

filter() 等方法中逗号隔开的条件是与的关系。 若是你须要执行更复杂的查询(例如OR语句),你可使用Q对象。

咱们能够组合& 和| 操做符以及使用括号进行分组来编写任意复杂的Q 对象。

同时,Q 对象可使用~ 操做符取反,这容许组合正常的查询和取反(NOT) 查询。

from django.db.models import Q

经典案例:

# 查询书籍名称是三国演义或者价格是444.44
# res = models.Book.objects.filter(title='三国演义',price=444.44)  # filter只支持and关系
# res1 = models.Book.objects.filter(Q(title='三国演义'),Q(price=444))  # 若是用逗号 那么仍是and关系
# res2 = models.Book.objects.filter(Q(title='三国演义')|Q(price=444))
# res3 = models.Book.objects.filter(~Q(title='三国演义')|Q(price=444))
# print(res2)


# Q高级用法
q = Q()
q.connector = 'or'  # 修改查询条件的关系   默认是and
q.children.append(('title__contains','三国演义'))  # 往列表中添加筛选条件
q.children.append(('price__gt',444))  # 往列表中添加筛选条件
res = models.Book.objects.filter(q)  # filter支持你直接传q对象  可是默认仍是and关系
print(res)

我的口水总结:

​ 或的时候和取反时候考虑, 对应 ~ , |

​ 能够用Q() 建立对象方法实现对象q q.children为一个列表,给当前列表添加2参数的元组,做为对应的搜索条件, 默认为 and 关系 , 设置 q的 connector = "or" 就可使之称为 或关系

事务

何为事务:

ACID
        原子性     都为最小的不可分割的单位,要么成功要么失败
        一致性     从一个一致性状态到另外一个一致性状态
        隔离性     各个数据操做之间是隔离的, 并发操做并不会影响到其余的数据的操做
        持久性     当提交以后数据的状态就永久肯定恒定,不会由于回退或取消而发送变化

with transaction.atomic():

在里面进行相关操做,本质就是一个锁

from django.db import transaction   # 哈哈哈, 也是从 django.db导入哩 
with transaction.atomic():
    """数据库操做
    在该代码块中书写的操做 同属于一个事务
    """
    models.Book.objects.create()
    models.Publish.objects.create()
    # 添加书籍和出版社 就是同一个事务 要么一块儿成功要么一块儿失败
print('出了 代码块 事务就结束')

我的总结:

跟数据相关, ORM操做的相关方法, 通常都是从 from django.db import 导入

数据库查询优化

orm内全部的语句操做都是 惰性查询:

只会在你真正须要数据的时候才会走数据库,若是你单单只写orm语句时不会走数据库的

这样设计的好处 在于 减轻数据库的压力

only与defer

案例:

# res = models.Book.objects.all()
# print(res)


# res = models.Book.objects.values('title')
# # print(res)
# for r in res:
#     print(r.title)


# res = models.Book.objects.only('title')
# # print(res)
# for r in res:
#     # print(r.title)  # 只走一次数据库查询
#     print(r.price)  # 当你点击一个不是only括号内指定的字段的时候 不会报错 而是会频繁的走数据库查询


# res1 = models.Book.objects.defer('title')  # defer与only是相反的
# for r in res1:  # defer会将不是括号内的全部的字段信息 所有查询出来封装对象中
#     # 一旦你点击了括号内的字段  那么会频繁的走数据库查询
#     print(r.price)

我的口水总结:

deferonly相反的

  1. only在查对应字段时候只走一次数据查询
  2. 当查询不是only括号内指定的字段的时候 不会报错 而是会频繁的走数据库查询
  3. defer会将不是括号内的全部的字段信息 所有查询出来封装对象中,只走一次
  4. 而defer对于括号内的字段 那么会频繁的走数据库查询

select_releated与prefect_releated

# select_related帮你直接连表操做 查询数据   括号内只能放外键字段
# res = models.Book.objects.all().select_related('publish')
# for r in res:
#     print(r.publish.name)
# res = models.Book.objects.all().select_related('publish__xxx__yyy__ttt')
# print(res)
# res = models.Book.objects.all()
"""
select_related:会将括号内外键字段所关联的那张表  直接所有拿过来(能够一次性拿多张表)跟当前表拼接操做
从而下降你跨表查询 数据库的压力

注意select_related括号只能放外键字段(一对一和一对多)
res = models.Book.objects.all().select_related('外键字段1__外键字段2__外键字段3__外键字段4')
"""
# prefetch_related  不主动连表
res = models.Book.objects.prefetch_related('publish')
"""
不主动连表操做(可是内部给你的感受像是连表操做了)  而是将book表中的publish所有拿出来  在取publish表中将id对应的全部的数据取出
res = models.Book.objects.prefetch_related('publish')
括号内有几个外键字段 就会走几回数据库查询操做    
"""
for r in res:
print(r.publish.name)

我的口水总结:

select_related:

会将括号内外键字段所关联的那张表 直接所有拿过来(能够一次性拿多张表)跟当前表拼接操做
从而下降你跨表查询 数据库的压力

select_related括号只能放外键字段(一对一和一对多)

prefetch_related:

不主动连表操做(可是内部给你的感受像是连表操做了) 而是将book表中的publish所有拿出来 在取publish表中将id对应的全部的数据取出

括号内有几个外键字段 就会走几回数据库查询操做

数据库建立字段

常见字段(跟mysql字段比较)

  1. AutoField() int primary key auto_increment
  2. CharField() varchar()
  3. IntegerField() int()
  4. BigIntegerField() 存放整数长度big型的类型
  5. EmailField() varchar(254)
  6. DateField() date
  7. DateTimeField() datetime
    • auto_now: 每次修改数据都会更新时间
    • auto_now_add: 只在第一次建立数据的时候才会更新一次
  8. BooleanField(Field)
    • is_delete = BooleanField()
    • 给该字段传值的时候 你只须要传布尔值便可
    • 可是对应到数据库 它存的是0和1
  9. TextField(Field)
    • 文本类型
      用来存大段文本
  10. FileField(Field)
    • 字符串,路径保存在数据库,文件上传到指定目录
    • 参数:
      upload_to = "" 用户上传的文件会自动放到等号后面指定的文件路径中
      storage = None 存储组件,默认django.core.files.storage.FileSystemStorage

自定义char字段

class MyChar(models.Field):
    def __init__(self,max_length,*args,**kwargs):
        self.max_length = max_length
        super().__init__(max_length=max_length,*args,**kwargs)

    def db_type(self, connection):
        return 'char(%s)'%self.max_length

django2注意点:

外键字段
当你在使用django2.X版本的时候 在创建外键关系时(*****)
须要你手动添加几个关键点参数
    models.cascade
    db_constraints
相关文章
相关标签/搜索