一、django默认支持sqlite,mysql, oracle,postgresql数据库。javascript
<1> sqlitephp
django默认使用sqlite的数据库,默认自带sqlite的数据库驱动 , 引擎名称:django.db.backends.sqlite3css
<2> mysqlhtml
引擎名称:django.db.backends.mysql前端
二、mysql驱动程序java
三、在django的项目中会默认使用sqlite数据库,在settings里有以下设置:python
建立数据库表的步骤:mysql
一、建立model;jquery
二、建立生成数据库的py文件:python manage.py makemigrations ;linux
三、建立数据库表:python manage.py migrate;
注意:记得在settings里的INSTALLED_APPS中加入'app01',而后再同步数据库。
打开pycharm右侧的databases,把建立好的db.sqlite3数据库拖过去,就能够操做数据库表了,这里咱们不用操心数据库名的问题;
若是咱们想要使用别的数据库,好比Mysql须要修改以下:
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'books', #你的数据库名称 'USER': 'root', #你的数据库用户名 'PASSWORD': '', #你的数据库密码 'HOST': '', #你的数据库主机,留空默认为localhost 'PORT': '3306', #你的数据库端口 } }
注意:
NAME即数据库的名字,在mysql链接前该数据库必须已经建立,而上面的sqlite数据库下的db.sqlite3则是项目自动建立 USER和PASSWORD分别是数据库的用户名和密码。 设置完后,再启动咱们的Django项目前,咱们须要激活咱们的mysql。 而后,启动项目,会报错:no module named MySQLdb 这是由于django默认你导入的驱动是MySQLdb,但是MySQLdb对于py3有很大问题,因此咱们须要的驱动是PyMySQL 因此,咱们只须要找到项目名文件下的__init__,在里面写入: import pymysql pymysql.install_as_MySQLdb() 问题解决!
四、ORM(对象关系映射)
关系对象映射(Object Relational Mapping,简称ORM),用于实现面向对象编程语言里不一样类型系统的数据之间的转换,换言之,就是用面向对象的方式去操做数据库的建立表以及增删改查等操做。
优势: 一、ORM使得咱们的通用数据库交互变得简单易行,并且彻底不用考虑该死的SQL语句。快速开发,由此而来。
二、能够避免一些新手程序猿写sql语句带来的性能问题。
缺点:一、性能有所牺牲,不过如今的各类ORM框架都在尝试各类方法,好比缓存,延迟加载登来减轻这个问题。效果很显著。
二、对于个别复杂查询,ORM仍然力不从心,为了解决这个问题,ORM通常也支持写raw sql。
三、经过QuerySet的query属性查询对应操做的sql语句
author_obj=models.Author.objects.filter(id=2) print(author_obj.query)
from django.db import models class Userinfo(models.Model): name = models.CharField(max_length=30) email = models.EmailField() memo = models.TextField()
<1> CharField #字符串字段, 用于较短的字符串. #CharField 要求必须有一个参数 maxlength, 用于从数据库层和Django校验层限制该字段所容许的最大字符数. <2> IntegerField #用于保存一个整数. <3> FloatField # 一个浮点数. 必须 提供两个参数: # # 参数 描述 # max_digits 总位数(不包括小数点和符号) # decimal_places 小数位数 # 举例来讲, 要保存最大值为 999 (小数点后保存2位),你要这样定义字段: # # models.FloatField(..., max_digits=5, decimal_places=2) # 要保存最大值一百万(小数点后保存10位)的话,你要这样定义: # # models.FloatField(..., max_digits=19, decimal_places=10) # admin 用一个文本框(<input type="text">)表示该字段保存的数据. <4> AutoField # 一个 IntegerField, 添加记录时它会自动增加. 你一般不须要直接使用这个字段; # 自定义一个主键:my_id=models.AutoField(primary_key=True) # 若是你不指定主键的话,系统会自动添加一个主键字段到你的 model. <5> BooleanField # A true/false field. admin 用 checkbox 来表示此类字段. <6> TextField # 一个容量很大的文本字段. # admin 用一个 <textarea> (文本区域)表示该字段数据.(一个多行编辑框). <7> EmailField # 一个带有检查Email合法性的 CharField,不接受 maxlength 参数. <8> DateField # 一个日期字段. 共有下列额外的可选参数: # Argument 描述 # auto_now 当对象被保存时,自动将该字段的值设置为当前时间.一般用于表示 "last-modified" 时间戳. # auto_now_add 当对象首次被建立时,自动将该字段的值设置为当前时间.一般用于表示对象建立时间. #(仅仅在admin中有意义...) <9> DateTimeField # 一个日期时间字段. 相似 DateField 支持一样的附加选项. <10> ImageField # 相似 FileField, 不过要校验上传对象是不是一个合法图片.#它有两个可选参数:height_field和width_field, # 若是提供这两个参数,则图片将按提供的高度和宽度规格保存. <11> FileField # 一个文件上传字段. #要求一个必须有的参数: upload_to, 一个用于保存上载文件的本地文件系统路径. 这个路径必须包含 strftime #formatting, #该格式将被上载文件的 date/time #替换(so that uploaded files don't fill up the given directory). # admin 用一个<input type="file">部件表示该字段保存的数据(一个文件上传部件) . #注意:在一个 model 中使用 FileField 或 ImageField 须要如下步骤: #(1)在你的 settings 文件中, 定义一个完整路径给 MEDIA_ROOT 以便让 Django在此处保存上传文件. # (出于性能考虑,这些文件并不保存到数据库.) 定义MEDIA_URL 做为该目录的公共 URL. 要确保该目录对 # WEB服务器用户账号是可写的. #(2) 在你的 model 中添加 FileField 或 ImageField, 并确保定义了 upload_to 选项,以告诉 Django # 使用 MEDIA_ROOT 的哪一个子目录保存上传文件.你的数据库中要保存的只是文件的路径(相对于 MEDIA_ROOT). # 出于习惯你必定很想使用 Django 提供的 get_<#fieldname>_url 函数.举例来讲,若是你的 ImageField # 叫做 mug_shot, 你就能够在模板中以 {{ object.#get_mug_shot_url }} 这样的方式获得图像的绝对路径. <12> URLField # 用于保存 URL. 若 verify_exists 参数为 True (默认), 给定的 URL 会预先检查是否存在( 即URL是否被有效装入且 # 没有返回404响应). # admin 用一个 <input type="text"> 文本框表示该字段保存的数据(一个单行编辑框) <13> NullBooleanField # 相似 BooleanField, 不过容许 NULL 做为其中一个选项. 推荐使用这个字段而不要用 BooleanField 加 null=True 选项 # admin 用一个选择框 <select> (三个可选择的值: "Unknown", "Yes" 和 "No" ) 来表示这种字段数据. <14> SlugField # "Slug" 是一个报纸术语. slug 是某个东西的小小标记(短签), 只包含字母,数字,下划线和连字符.#它们一般用于URLs # 若你使用 Django 开发版本,你能够指定 maxlength. 若 maxlength 未指定, Django 会使用默认长度: 50. #在 # 之前的 Django 版本,没有任何办法改变50 这个长度. # 这暗示了 db_index=True. # 它接受一个额外的参数: prepopulate_from, which is a list of fields from which to auto-#populate # the slug, via JavaScript,in the object's admin form: models.SlugField # (prepopulate_from=("pre_name", "name"))prepopulate_from 不接受 DateTimeFields. <13> XMLField #一个校验值是否为合法XML的 TextField,必须提供参数: schema_path, 它是一个用来校验文本的 RelaxNG schema #的文件系统路径. <14> FilePathField # 可选项目为某个特定目录下的文件名. 支持三个特殊的参数, 其中第一个是必须提供的. # 参数 描述 # path 必需参数. 一个目录的绝对文件系统路径. FilePathField 据此获得可选项目. # Example: "/home/images". # match 可选参数. 一个正则表达式, 做为一个字符串, FilePathField 将使用它过滤文件名. # 注意这个正则表达式只会应用到 base filename 而不是 # 路径全名. Example: "foo.*\.txt^", 将匹配文件 foo23.txt 却不匹配 bar.txt 或 foo23.gif. # recursive可选参数.要么 True 要么 False. 默认值是 False. 是否包括 path 下面的所有子目录. # 这三个参数能够同时使用. # match 仅应用于 base filename, 而不是路径全名. 那么,这个例子: # FilePathField(path="/home/images", match="foo.*", recursive=True) # ...会匹配 /home/images/foo.gif 而不匹配 /home/images/foo/bar.gif <15> IPAddressField # 一个字符串形式的 IP 地址, (i.e. "24.124.1.30"). <16># CommaSeparatedIntegerField # 用于存放逗号分隔的整数值. 相似 CharField, 必需要有maxlength参数.
<1> null : 数据库中字段是否能够为空 <2> blank: django的 Admin 中添加数据时是否可容许空值 <3> default:设定缺省值 <4> editable:若是为假,admin模式下将不能改写。缺省为真 <5> primary_key:设置主键,若是没有设置django建立表时会自动加上: id = meta.AutoField('ID', primary_key=True) primary_key=True implies blank=False, null=False and unique=True. Only one primary key is allowed on an object. <6> unique:数据惟一 <7> verbose_name Admin中字段的显示名称 <8> validator_list:有效性检查。非有效产生 django.core.validators.ValidationError 错误 <9> db_column,db_index 若是为真将为此字段建立索引 <10>choices:一个用来选择值的2维元组。第一个值是实际存储的值,第二个用来方便进行选择。 如SEX_CHOICES= (( ‘F’,'Female’),(‘M’,'Male’),) gender = models.CharField(max_length=2,choices = SEX_CHOICES)
a、基本操做
---------------------增(create , save) --------------------- from app01.models import * #create方式一: Author.objects.create(name='Alvin') #create方式二: Author.objects.create(**{"name":"alex"}) #save方式一: author=Author(name="alvin") author.save() #save方式二: author=Author() author.name="alvin" author.save() ---------------------删(delete) ------------------------- Book.objects.filter(id=1).delete() ---------------------改(update和save)------------------ # 方法一,get只能获得一个对象 book = Book.objects.get(author='charlie') book.price = 200 book.save() # 方法二,推荐使用 Book.objects.filter(name='python').update(price=100) 注意: <1> 第二种方式修改不能用get的缘由是:update是QuerySet对象的方法, get返回的是一个model对象,它没有update方法,而filter返回的是一个QuerySet对象 (filter里面的条件可能有多个条件符合,好比name='alvin',可能有两个name='alvin'的行数据)。 <2>在“插入和更新数据”小节中,咱们有提到模型的save()方法,这个方法会 更新一行里的全部列。 而某些状况下,咱们只须要更新行里的某几列。 ---------------------查(filter,value等) ---------------------- # <1>filter(**kwargs): 它包含了与所给筛选条件相匹配的对象 # <2>all(): 查询全部结果 # <3>get(**kwargs): '''返回与所给筛选条件相匹配的对象,返回结果有且只有一个,若是符合筛选条件的 #对象超过一个或者没有都会抛出错误。''' #下面的方法都是对查询的结果再进行处理:好比 objects.filter.values()-------- # <4>values(*field): '''返回一个ValueQuerySet——一个特殊的QuerySet, 运行后获得的并非一系列 model的实例化对象,而是一个可迭代的字典序列''' # <5>exclude(**kwargs): 它包含了与所给筛选条件不匹配的对象 # <6>order_by(*field): 对查询结果排序 # <7>reverse(): 对查询结果反向排序 # <8>distinct(): 从返回结果中剔除重复纪录,all()后面跟去重没用,由于ID没有重复,只有在values()后面跟去重才有用,对某一个字段进行去重; # <9>values_list(*field): 它与values()很是类似,它返回的是一个元组序列,values返回的是一个字典序列 # <10>count(): 返回数据库中匹配查询(QuerySet)的对象数量。 # <11>first(): 返回第一条记录 # <12>last(): 返回最后一条记录 # <13>exists(): 若是QuerySet包含数据,就返回True,不然返回False。
实例:
models文件内容
from django.db import models class Book(models.Model): name = models.CharField(max_length=20) price = models.IntegerField() pub_data = models.DateField() author = models.CharField(max_length=20,null=False) def __str__(self): return self.name class Author(models.Model): name = models.CharField(max_length=20) age = models.IntegerField()
view文件内容
#view.py def addbook(request): # 添加表记录方法一 book = Book(name='python',price=99,pub_data='2018-2-8',author='charlie') book.save() # 方法二 Book.objects.create(name='php',price=88,pub_data='2018-10-8',author='oldboy') # Book.objects.create(**dic)添加字典内容 return HttpResponse('添加成功') def update(request): # 方法一,get用于只获得一个对象,若是对象重复就报错 # 这种方法会把全部的字段都从新赋值,效率低 book = Book.objects.get(id=1) book.price = 998 book.save() # 方法二,推荐使用,update是Queryset方法,只更新查询到的记录 Book.objects.filter(name='python').update(price=150) return HttpResponse('修改为功') def select(request): book_list = Book.objects.all() book_list = Book.objects.all()[:3]#前三个 book_list = Book.objects.all()[::2]#每两个取一个 book_list = Book.objects.all()[::-1]#倒着取 # value只取指定的字段,获得一个查询集,内容是一个字典 ret = Book.objects.filter(name='charlie').values('name','price') # 获得一个查询集,内容是一个列表,列表元素是元组 ret = Book.objects.filter(name='charlie').values_list('name','price') return render(request,'index.html',locals())
LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'console':{ 'level':'DEBUG', 'class':'logging.StreamHandler', }, }, 'loggers': { 'django.db.backends': { 'handlers': ['console'], 'propagate': True, 'level':'DEBUG', }, } }
b、利用双下划线方法进行模糊匹配
# 获取个数 models.Tb1.objects.filter(name='seven').count() # 大于,小于 # models.Tb1.objects.filter(id__gt=1) # 获取id大于1的值 models.Tb1.objects.filter(id__gte=1) # 获取id大于等于1的值 models.Tb1.objects.filter(id__lt=10) # 获取id小于10的值 models.Tb1.objects.filter(id__lte=10) # 获取id小于10的值 models.Tb1.objects.filter(id__lt=10, id__gt=1) # 获取id大于1 且 小于10的值 # in # models.Tb1.objects.filter(id__in=[11, 22, 33]) # 获取id等于十一、2二、33的数据 models.Tb1.objects.exclude(id__in=[11, 22, 33]) # not in # isnull Entry.objects.filter(pub_date__isnull=True) #某字段是否能够为空 # contains # models.Tb1.objects.filter(name__contains="ven") #内容里包含某字符串 models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感 models.Tb1.objects.exclude(name__icontains="ven") #内容里没有某字段(不区分大小写) # range # models.Tb1.objects.filter(id__range=[1, 2]) # 范围bettwen and # 其余相似 # startswith,istartswith(不区分大小写), endswith, iendswith, # order by # models.Tb1.objects.filter(name='seven').order_by('id') # asc models.Tb1.objects.filter(name='seven').order_by('-id') # desc,反向 # group by # from django.db.models import Count, Min, Max, Sum models.Tb1.objects.filter(c1=1).values('id').annotate(c=Count('num')) SELECT "app01_tb1"."id", COUNT("app01_tb1"."num") AS "c" FROM "app01_tb1" WHERE "app01_tb1"."c1" = 1 GROUP BY "app01_tb1"."id" # limit 、offset # models.Tb1.objects.all()[10:20] # regex正则匹配,iregex 不区分大小写 # Entry.objects.get(title__regex=r'^(An?|The) +') Entry.objects.get(title__iregex=r'^(an?|the) +') # date # Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1)) Entry.objects.filter(pub_date__date__gt=datetime.date(2005, 1, 1)) # year # Entry.objects.filter(pub_date__year=2005) Entry.objects.filter(pub_date__year__gte=2005) # month # Entry.objects.filter(pub_date__month=12) Entry.objects.filter(pub_date__month__gte=6) # day # Entry.objects.filter(pub_date__day=3) Entry.objects.filter(pub_date__day__gte=3) # week_day # Entry.objects.filter(pub_date__week_day=2) Entry.objects.filter(pub_date__week_day__gte=2) # hour # Event.objects.filter(timestamp__hour=23) Event.objects.filter(time__hour=5) Event.objects.filter(timestamp__hour__gte=12) # minute # Event.objects.filter(timestamp__minute=29) Event.objects.filter(time__minute=46) Event.objects.filter(timestamp__minute__gte=29) # second # Event.objects.filter(timestamp__second=31) Event.objects.filter(time__second=2) Event.objects.filter(timestamp__second__gte=31)
c、连表操做(了不得的双下划线)
#Django升级到2版本以后models.ForeignKey()须要填写on_delect参数 on_delete=models.CASCADE, # 删除关联数据,与之关联也删除 on_delete=models.DO_NOTHING, # 删除关联数据,什么也不作,最好不要; on_delete=models.PROTECT, # 删除关联数据,引起错误ProtectedError on_delete=models.SET_DEFAULT #设置默认值,前提是ForeignKey必须设置的默认值; on_delete=models.SET(...) '''设置给定值,或者若是传入了callable,则调用它的结果。 在大多数状况下,为了不在导入models.py时执行查询,必须传递callable''' on_delete=models.SET_NULL, # 删除关联数据,与之关联的值设置为null(前提FK字段须要设置为可空,一对一同理) # 例如:models.ForeignKey('关联表', on_delete=models.SET_NULL, blank=True, null=True)
#models.py from django.db import models class Book(models.Model): '''书籍''' name = models.CharField(max_length=20) price = models.IntegerField() pub_data = models.DateField() publish = models.ForeignKey('Publish',on_delete=models.CASCADE) def __str__(self): return self.name class Author(models.Model): '''做者''' name = models.CharField(max_length=20) class Publish(models.Model): '''出版社''' #一对多的外键要加在多的哪一张表里,一本书只能有一个出版社 name = models.CharField(max_length=30) city = models.CharField(max_length=30)
from django.shortcuts import render,HttpResponse from app.models import * #-----------------------增----------------------- # 方式一,给publish_id赋值,django默认给你的外键字段添加_id Book.objects.create(name='linux运维',price=88,pub_data='2016-10-8',publish_id=1) # 方式二,给publish字段直接赋值,须要先获得一个对象 pulish_obj = Publish.objects.filter(name='西湖出版社')[0] Book.objects.create(name='Java', price=188, pub_data='2016-1-23', publish=pulish_obj) #-----------------------删----------------------- Book.objects.filter(publish__name='西湖出版社').delete() #-----------------------改----------------------- Book.objects.filter(publish__name='人民出版社').update(price=300) #-----------------------查----------------------- #第一种方式:经过对象(不推荐使用) # 正向 book_obj = Book.objects.get(name='python') publish_obj = book_obj.publish publish_name = publish_obj.name #反向 pub_obj = Publish.objects.filter(name='西湖出版社')[0] #下面的到一个查询集,内容是字典,取第一个字典 pub_name = pub_obj.book_set.all().values('name','price')[0] #第二种方式:经过filter(__),推荐使用 #正向查询,指定出版社的书籍信息;从有外键的表查询为正向; book_name = Book.objects.filter(publish__name='西湖出版社').values('name','price')[0] # 反向查询,指定书籍的出版社信息,经过 类名__字段名 pub_name = Publish.objects.filter(book__name='python').values('name','city')[0] #经过value(__) book_name = Book.objects.filter(name='Java').values('publish__name','publish__city')[0] # 查询全部在北京的出版社出版的书籍,获得一个列表 book_list = Publish.objects.filter(city='北京').values('book__name') book_list2 = Book.objects.filter(publish__city='北京').values('name')
from django.db import models class Book(models.Model): '''书籍''' name = models.CharField(max_length=20) price = models.IntegerField() pub_data = models.DateField() # 一对多,一本书只能有一个出版社 publish = models.ForeignKey('Publish',on_delete=models.CASCADE) # 多对多,一本书能够有多个做者,系统会自动建立第三张表:app_book_authors #可是不能直接对这张表进行操做,由于没有它的类 authors = models.ManyToManyField('Author') def __str__(self): return self.name class Publish(models.Model): '''出版社''' #一对多的外键要加在多的哪一张表里,一本书只能有一个出版社 name = models.CharField(max_length=30) city = models.CharField(max_length=30) def __str__(self): return self.name class Author(models.Model): '''做者''' name = models.CharField(max_length=20) age = models.IntegerField(default=20) def __str__(self): return self.name
from django.shortcuts import render,HttpResponse from app.models import * from django.db.models import Avg,Min,Max,Count,Sum #------------------增-------------------- #方式一 book_obj = Book.objects.get(id=4) book_obj.authors.add(1) book_obj.authors.add(*[2,3,]) #方式二 book_obj = Book.objects.get(id=4) author_objs = Author.objects.all() book_obj.authors.add(*author_objs) #------------------删-------------------- # 先获得一个书籍对象 book_obj = Book.objects.get(id=4) #删除 book_obj.authors.remove(3) book_obj.authors.remove(*[1,2,]) #清空 book_obj.authors.clear() #------------------改-------------------- #重置,以设置的为准,这里列表不加*,已经有的不动,新内容里没有的就删除 book_obj.authors.set([1,2,3]) #------------------查-------------------- #正向,找到id=2的书籍的全部做者 book_obj = Book.objects.get(id=2) book_obj.authors.all() #方式二 Book.objects.filter(id=2).values('authors__name') # 反向,找到做者的全部书籍 author_obj = Author.objects.get(id=1) author_obj.book_set.all() #全部书籍名称和对应的做者名 book_list = Book.objects.all().values('name','authors__name') #列举做者charlie的全部书籍 Book.objects.filter(authors__name="charlie") #-----------------聚合函数aggregate()------------------ #求全部书籍的平均价格 ret = Book.objects.all().aggregate(Avg('price'))#{'price__avg': 122.0} # 也能够自定义名称,结果:{'avg_price': 122.0} ret = Book.objects.all().aggregate(avg_price=Avg('price')) #做者Charlie出的全部书,Count参数能够是book表里任意个字段,只是查有几条记录,查谁都同样 ret = Book.objects.filter(authors__name='charlie').aggregate(Count('name')) #-----------------分组函数annotate--------------------- #按做者名分组,求每一个做者全部书籍的价格总和 ret = Book.objects.values('authors__name').annotate(Sum('price')) ''' <QuerySet [{'authors__name': 'charlie', 'price__sum': 366}, {'authors__name': 'alex', 'price__sum': 88}, {'authors__name': 'james', 'price__sum': 188}]> ''' #求每一个出版社的最低价格的书 ret = Publish.objects.values('name').annotate(Min('book__price'))
#去掉authors = models.ManyToManyField('Author') #建立新类 class Book_Author(models.Model): #第三张表 book = models.ForeignKey('Book',on_delete=models.CASCADE) author = models.ForeignKey('Author',on_delete=models.CASCADE) def __str__(self): return self.name #添加 Book_Author.objects.create(book_id=3,author_id=1) # 利用对象查询 book_obj = Book.objects.get(id=2) author_name = book_obj.book_author_set.all()[0].author print(author_name) #双下划线查询 book_list1 = Author.objects.filter(name='charlie').values('book_author__book') print(book_list1) book_list2 = Book.objects.filter(book_author__author__name='charlie').values('name') print(book_list2)
from django.db.models import F,Q #F 使用查询条件的值,专门取对象中某列值的操做 #给全部的书价格加10 Book.objects.all().update(price=F('price') + 10) # Q 进行条件或的查询,| 或,知足任意条件 ret = Book.objects.filter(Q(price=110)|Q(name='GO')) #~ 非,不知足条件 ret = Book.objects.filter(~Q(name='GO')) #书籍名称中包含某个字母 ret = Book.objects.filter(Q(name__contains='J')) #Q查询和关键字查询结合使用,Q查询必定要放前面 ret = Book.objects.filter(Q(price=110),name='GO') print(ret) #Q(name__startswith='P') 书籍名称以字母P开头
三、补充整理
跨多张表查询,能够连续使用双下划线
class Book(models.Model): '''书籍''' name = models.CharField(max_length=20) price = models.IntegerField() pub_data = models.DateField() authors = models.ForeignKey('Author',on_delete=models.CASCADE) class Author(models.Model): '''做者''' name = models.CharField(max_length=20) age = models.IntegerField(default=20) country = models.ForeignKey('Country',on_delete=models.CASCADE) class Country(models.Model): '''国家''' name = models.CharField(max_length=20) #全部中国籍做者出的全部书籍 Book.objects.filter(authors__country__name='China') #正向查找:Book.objects.filter(authors__name='charlie') #反向查找: obj = Authors.objects.filter(name='charlie').first() obj.book_set.all() #没有外键的表里其实隐藏了一个字段:类名_set,也能够修改这个字段名 class Book(models.Model): authors = models.ForeignKey('Author',on_delete=models.CASCADE,related_name='book') obj = Authors.objects.filter(name='charlie').first() book_list = obj.book.all()#做者对应的全部书籍对象查询集[obj(name,price,..),obj(name,price,..),]
所谓惰性机制:Publisher.objects.all()或者.filter()等都只是返回了一个QuerySet(查询结果集对象),它并不会立刻执行sql,而是当调用QuerySet的时候才执行。
QuerySet特色:<1> 可迭代的;<2> 可切片
QuerySet的高效使用:
<1>Django的queryset是惰性的 Django的queryset对应于数据库的若干记录(row),经过可选的查询来过滤。例如,下面的代码会得 到数据库中名字为‘Dave’的全部的人:person_set = Person.objects.filter(first_name="Dave") 上面的代码并无运行任何的数据库查询。你可使用person_set,给它加上一些过滤条件,或者将它传给某个函数, 这些操做都不会发送给数据库。这是对的,由于数据库查询是显著影响web应用性能的因素之一。 <2>要真正从数据库得到数据,你能够遍历queryset或者使用if queryset,总之你用到数据时就会执行sql. 为了验证这些,须要在settings里加入 LOGGING(验证方式) obj=models.Book.objects.filter(id=3) for i in obj: print(i) if obj: print("ok") <3>queryset是具备cache的 当你遍历queryset时,全部匹配的记录会从数据库获取,而后转换成Django的model。这被称为执行 (evaluation).这些model会保存在queryset内置的cache中,这样若是你再次遍历这个queryset, 你不须要重复运行通用的查询。可是若是你修改了数据库,就须要再查询一次,不然你查到的仍是上次的
缓存数据。 obj=models.Book.objects.filter(id=3) for i in obj: print(i) models.Book.objects.filter(id=3).update(title="GO") obj_new=models.Book.objects.filter(id=3) for i in obj: print(i) #LOGGING只会打印一次 <4>简单的使用if语句进行判断也会彻底执行整个queryset而且把数据放入cache,虽然你并不须要这些 数据!为了不这个,能够用exists()方法来检查是否有数据: obj = Book.objects.filter(id=4) # exists()的检查能够避免数据放入queryset的cache。 if obj.exists(): print("hello world!") <5>当queryset很是巨大时,cache会成为问题 处理成千上万的记录时,将它们一次装入内存是很浪费的。更糟糕的是,巨大的queryset可能会锁住系统 进程,让你的程序濒临崩溃。要避免在遍历数据的同时产生queryset cache,可使用iterator()方法 来获取数据,处理完数据就将其丢弃。 objs = Book.objects.all().iterator() # iterator()能够一次只从数据库获取少许数据,这样能够节省内存 for obj in objs: print(obj.name) #BUT,再次遍历没有打印,由于迭代器已经在上一次遍历(next)到最后一次了,没得遍历了 for obj in objs: print(obj.name) 固然,使用iterator()方法来防止生成cache,意味着遍历同一个queryset时会重复执行查询。因此使 用iterator()的时候要小心,确保你的代码在操做一个大的queryset时没有重复执行查询。 总结: queryset的cache是用于减小程序对数据库的查询,在一般的使用下会保证只有在须要的时候才会查询数据库。 使用exists()和iterator()方法能够优化程序对内存的使用。不过,因为它们并不会生成queryset cache,可能 会形成额外的数据库查询。总之一句话,若是查询数据量巨大时,使用迭代器;若是数据量很小,并且又须要重复查询
时,使用查询集。
admin是django强大功能之一,它能从数据库中读取数据,呈如今页面中,进行管理。默认状况下,它的功能已经很是强大,若是你不须要复杂的功能,它已经够用,可是有时候,一些特殊的功能还须要定制,好比搜索功能,下面这一系列文章就逐步深刻介绍如何定制适合本身的admin应用。
若是你以为英文界面很差用,能够在setting.py 文件中修改如下选项:
LANGUAGE_CODE = 'en-us' #LANGUAGE_CODE = 'zh-hans'
使用django admin 则须要如下步骤:
一、建立后台管理员
python manage.py createsuperuser
二、配置后台管理url
url(r'^admin/', admin.site.urls)
三、注册和配置django admin 后台管理页面
a、注册:在admin中执行以下配置
from django.contrib import admin from app01 import models admin.site.register(models.UserType) admin.site.register(models.UserInfo) admin.site.register(models.UserGroup) admin.site.register(models.Asset)
b、设置数据表字段的显示名称
#方式一 class UserType(models.Model): name = models.CharField(max_length=50) #meta表示数据库中显示的信息 class Meta: db_table = 'UserType' #数据库表名 verbose_name = '用户类型' #数据库字段名 verbose_name_plural = '用户类型' #方式二 #修改models class Book(models.Model): '''书籍''' name = models.CharField(max_length=20,verbose_name='名称') price = models.IntegerField('价格')
c、自定义页面展现
from django.contrib import admin from app.models import * #自定制admin类 class BookAdmin(admin.ModelAdmin): #不能够显示多对多的关联字段,注意这些都是元组,注意末尾的逗号 list_display = ('id','name','price','pub_date',) #可编辑 # list_editable = ('name','price','pub_date',) # 设置字段可垂直搜索 filter_horizontal = ('authors',) #设置每页显示的条目 # list_per_page = 2 #根据字段搜索,关联字段加__,三个字段中重复的部分都会被搜索到 search_fields = ('id','name','publish__name',) #根据字段过滤 list_filter = ('pub_date','publish',) #根据字段排序,能够多个字段,依次排序,‘-id’表示降序排 ordering = ('id',) #添加书籍时隐藏字段,列表里必须是元组 fieldsets = [ #默认显示name,fieldes是固定的,后面跟列表 (None,{'fields':['name',]}), #将下列字段以折叠的方式显示 ('other information',{'fields':['price','pub_date','publish'],'classes':['collapse',]}), ] #将模型注册到admin admin.site.register(Book,BookAdmin)
四、注册medel类到admin的两种方式:
admin.site.register(Book,MyAdmin)
@admin.register(Book)
五、装饰器使用方法
from django.contrib import admin from app01.models import * # Register your models here. # @admin.register(Book)#----->单给某个表加一个定制 class MyAdmin(admin.ModelAdmin): list_display = ("title","price","publisher") search_fields = ("title","publisher") list_filter = ("publisher",) ordering = ("price",) fieldsets =[ (None, {'fields': ['title']}), ('price information', {'fields': ['price',"publisher"], 'classes': ['collapse']}), ] admin.site.register(Book,MyAdmin) admin.site.register(Publish) admin.site.register(Author)
一、django中的Form通常有一下几种功能:
生成HTML标签
验证用户输入:ajax和form表单提交
HTML Form提交保留上次提交数据
初始化页面显示内容
二、用form表单提交的基本流程
a、在views.py中建立一个类:F(forms.Form),若是正规操做,应该是在APP中新建一个forms.py文件,再导入;
b、类中建立字段(包含正则表达式和HTML插件)
c、用户以GET方式请求:
把obj=F()发送到前端,这里不用传入任何参数
{{ obj.user }}...前端自动生成input标签
d、用户以POST方式请求:
obj = Form1(request.POST),对象中包含用户输入信息和错误信息
先验证用户输入是否正确,若是正确就跳转页面,若是错误就发送obj
{{ obj.errors.user.0 }} 前端显示错误信息
补充:
#mark_safe,不用加safe就能够直接渲染HTML标签 from django.utils.safestring import mark_safe txt = mark_safe("<input type='text'/") # novalidate忽略浏览器的验证 <form action="/add_user/" method="POST" novalidate> # 上传文件记得加上enctype <form action="/test/" method="POST" enctype="multipart/form-data" novalidate> # form表单自动生成input标签,编辑时将数据库查询内容做为input标签默认值 data = UserInfo.objects.filter(id=nid).first() obj = F1({'username':data.username,'email':data.email})
实例:
from django.shortcuts import render,redirect,HttpResponse from django import forms from django.forms import fields class Form1(forms.Form): user = fields.CharField( min_length=6, max_length=18, required=True, error_messages={ 'required':'用户名不能为空', 'min_length':'用户名过短', 'max_length':'用户名太长', } ) pwd = fields.CharField( min_length=10, required=True, error_messages = { 'required': '密码不能为空', 'min_length': '密码过短', } ) age = fields.IntegerField( required=True, error_messages={ 'required': '年龄不能为空', 'invalid': '必须为数字', } ) email = fields.EmailField( min_length=8, required=True, error_messages={ 'required': '邮箱不能为空', 'invalid': '格式错误' } ) def f1(request): if request.method == 'GET': #自动生成input标签 obj = Form1() return render(request,'f1.html',{'obj': obj}) if request.method == 'POST': obj = Form1(request.POST) #验证是否成功 if obj.is_valid(): #若是成功,打印用户提交的数据,跳转 print("验证成功",obj.cleaned_data) return redirect('http://www.baidu.com') else: print("验证失败", obj.errors) return render(request, 'f1.html', {'obj': obj})
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> {% load staticfiles %} </head> <body> <form action="/f1.html" method="POST" id="fm"> <p>用户名{{ obj.user }}{{ obj.errors.user.0 }}</p> <p>密码{{ obj.pwd }}{{ obj.errors.pwd.0 }}</p> <p>年龄{{ obj.age }}{{ obj.errors.age.0 }}</p> <p>邮箱{{ obj.email }}{{ obj.errors.email.0 }}</p> <input type="submit" value="提交"/> <input type="button" value="ajax提交" id="ajaxSubmit"/> </form> </body> </html>
三、建立Form类时,主要涉及到 【字段】 和 【插件】,字段用于对用户请求数据的验证,插件用于自动生成HTML;
Field required=True, 是否容许为空 widget=None, HTML插件 label=None, 用于生成Label标签或显示内容 initial=None, 初始值 help_text='', 帮助信息(在标签旁边显示) error_messages=None, 错误信息 {'required': '不能为空', 'invalid': '格式错误'} show_hidden_initial=False, 是否在当前插件后面再加一个隐藏的且具备默认值的插件(可用于检验两次输入是否一直) validators=[], 自定义验证规则 localize=False, 是否支持本地化 disabled=False, 是否能够编辑 label_suffix=None Label内容后缀 #字符串 CharField(Field) max_length=None, 最大长度 min_length=None, 最小长度 strip=True 是否移除用户输入空白 #整型 IntegerField(Field) max_value=None, 最大值 min_value=None, 最小值 #浮点型,十进制小数 DecimalField(IntegerField) max_value=None, 最大长度 min_value=None, 最小长度 max_digits=None, 总长度 decimal_places=None, 小数位长度 #下拉框 ChoiceField(Field) choices=(), 选项,如:choices = ((0,'上海'),(1,'北京'),) required=True, 是否必填 widget=None, 插件,默认select插件 label=None, Label内容 initial=None, 初始值 help_text='', 帮助提示 TypedChoiceField(ChoiceField) coerce = lambda val: val 对选中的值进行一次转换 empty_value= '' 空值的默认值 #多选框 MultipleChoiceField(ChoiceField) TypedMultipleChoiceField(MultipleChoiceField) coerce = lambda val: val 对选中的每个值进行一次转换 empty_value= '' 空值的默认值 #时间 DateField(BaseTemporalField) 格式:2015-09-01 TimeField(BaseTemporalField) 格式:11:12 DateTimeField(BaseTemporalField)格式:2015-09-01 11:12 #邮箱 EmailField(CharField) #自定制正则表达式 RegexField(CharField) regex, 自定制正则表达式 max_length=None, 最大长度 min_length=None, 最小长度 error_message=None, 忽略,错误信息使用 error_messages={'invalid': '...'} #上传文件 FileField(Field) allow_empty_file=False 是否容许空文件 ImageField(FileField) ... 注:须要PIL模块,pip3 install Pillow 以上两个字典使用时,须要注意两点: - form表单中 enctype="multipart/form-data" - view函数中 obj = MyForm(request.POST, request.FILES) #IP GenericIPAddressField protocol='both', both,ipv4,ipv6支持的IP格式 unpack_ipv4=False 解析ipv4地址,若是是::ffff:192.0.2.1时候,可解析为192.0.2.1, PS:protocol必须为both才能启用 #数据库操做相关字段 ModelChoiceField(ChoiceField) ... django.forms.models.ModelChoiceField queryset, # 查询数据库中的数据 empty_label="---------", # 默认空显示内容 to_field_name=None, # HTML中value的值对应的字段 limit_choices_to=None # ModelForm中对queryset二次筛选 ModelMultipleChoiceField(ModelChoiceField) ... django.forms.models.ModelMultipleChoiceField
#views.py from django.shortcuts import render from django import forms from django.forms import fields,widgets class TestForm(forms.Form): user = fields.CharField( required=True, min_length=3, max_length=12, error_messages={'required':'不能为空',}, #widgets定制HTML插件 # widget=widgets.Select, label = '用户名', label_suffix='->', ) age = fields.IntegerField(max_value=100,min_value=18) email = fields.EmailField() #FileField和ImageField功能同样 img = fields.FileField() #生成下拉框,initial默认选中,或者obj = TestForm({'city':2})也能够默认选择 city = fields.ChoiceField( choices=[(1,'北京'),(2,'上海'),], initial=2, ) #多选框 hobby = fields.MultipleChoiceField( choices=[(1,'足球'),(2,'篮球'),], initial=[1,2], ) country = fields.TypedChoiceField( choices=[(1, '中国'), (2, '美国'), ], initial=2, #将传入的参数作一个数据类型转换 'country': 2 coerce=lambda x:int(x), empty_value='null',#空值的默认值 ) def test(request): if request.method == 'GET': obj = TestForm() return render(request, 'test.html',locals()) else: #上传的文件在request.FILES中 obj = TestForm(request.POST,request.FILES) obj.is_valid() print(obj.cleaned_data) return render(request,'test.html',locals()) #test.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {#上传文件记得加上enctype#} <form action="/test/" method="POST" enctype="multipart/form-data" novalidate> {% csrf_token %} <p>{{ obj.user.label }}{{ obj.user }}</p> <p>{{ obj.age.label }}{{ obj.age }}</p> <p>{{ obj.email.label }}{{ obj.email }}</p> <p>{{ obj.img.label }}{{ obj.img }}</p> <p>{{ obj.city.label }}{{ obj.city }}</p> <p>{{ obj.hobby.label }}{{ obj.hobby }}</p> <p>{{ obj.country.label }}{{ obj.country }}</p> <input type="submit" value="提交"/> </form> {# 能够所有自动生成一个标签,可是页面排版没法控制,不建议使用#} {# {{ obj.as_p }}#} </body> </html>
四、Django内置插件
#widgets定制HTML插件,每个字段都有本身的默认插件,还能够定制属性 widget=widgets.TextInput(attrs={'class':'c1'}),
TextInput(Input) NumberInput(TextInput) EmailInput(TextInput) URLInput(TextInput) PasswordInput(TextInput) HiddenInput(TextInput) Textarea(Widget) DateInput(DateTimeBaseInput) DateTimeInput(DateTimeBaseInput) TimeInput(DateTimeBaseInput) CheckboxInput Select NullBooleanSelect SelectMultiple RadioSelect CheckboxSelectMultiple FileInput ClearableFileInput MultipleHiddenInput SplitDateTimeWidget SplitHiddenDateTimeWidget SelectDateWidget
五、经常使用的选择插件
# 单radio,值为字符串 user = fields.CharField( initial=2, widget=widgets.RadioSelect(choices=((1,'上海'),(2,'北京'),)) ) # 单radio,值为字符串 user = fields.ChoiceField( choices=((1, '上海'), (2, '北京'),), initial=2, widget=widgets.RadioSelect ) # 单select,值为字符串 user = fields.CharField( initial=2, widget=widgets.Select(choices=((1,'上海'),(2,'北京'),)) ) # 单select,值为字符串 user = fields.ChoiceField( choices=((1, '上海'), (2, '北京'),), initial=2, widget=widgets.Select ) # 多选select,值为列表 user = fields.MultipleChoiceField( choices=((1,'上海'),(2,'北京'),), initial=[1,], widget=widgets.SelectMultiple ) # 单checkbox user = fields.CharField( widget=widgets.CheckboxInput() ) # 多选checkbox,值为列表 user = fields.MultipleChoiceField( initial=[2, ], choices=((1, '上海'), (2, '北京'),), widget=widgets.CheckboxSelectMultiple )
六、在使用选择标签时,须要注意choices的选项是须要从数据库中获取,可是因为是静态字段 ***获取的值没法实时更新***,那么须要自定义__init__构造方法,方法给widget.choices从新从数据库中取值,这样每次页面一刷新就实例化一次form对象,就会执行一次init函数,也就是会从数据库中取一次值。
方式一:
from django.shortcuts import render from django import forms from django.forms import fields,widgets from app.models import *#models.py中的类 class UserInfo(models.Model): username = models.CharField(max_length=32) email = models.EmailField(max_length=32) def __str__(self): return self.username #建立自定义form class LoveForm(forms.Form): price = fields.IntegerField() user_id = fields.IntegerField(widget=widgets.Select) #静态字段 获取的值没法实时更新,须要自定义构造方法 def __init__(self,*args,**kwargs): # super必须在上面,它拷贝了全部的静态字段,下面才能去内部取字段 super(LoveForm,self).__init__(*args,**kwargs) self.fields['user_id'].widget.choices = UserInfo.objects.values_list('id','username') def love(request): obj = LoveForm() return render(request,'love.html',locals())
方式二:
#使用django提供的ModelChoiceField和ModelMultipleChoiceField字段来实现 from django.forms.models import ModelChoiceField class LoveForm(forms.Form): user_id2 = ModelChoiceField( #这里没法显示数据库表中的ID之外的字段,须要models中的类加上个__str__方法,返回某个字段 #虽然同样能够实时更新,可是与数据库的格式关系太大,不能灵活显示每一个字段,不建议使用 queryset=UserInfo.objects.all() )
七、使用ajax提交
没法自动跳转页面,就是在views函数中设置redirect,ajax也不会遵从,须要在前端ajax的回调函数本身使用js代码跳转,window.location.href = 'http://www.baidu.com'
错误信息须要本身显示到页面,obj.errors 类型<class 'django.forms.utils.ErrorDict'>继承dict,因此用json.dumps()不会报错。
class AjaxForm(forms.Form): price = fields.IntegerField() user_id = fields.IntegerField( widget=widgets.Select(choices=[(1, '中国'), (2, '美国'), ],) ) def ajax(request): if request.method == 'GET': #自动生成input标签,并无开始作验证 obj = AjaxForm() return render(request,'ajax.html',{'obj': obj}) if request.method == 'POST': import json response = {'status':True,'msg':None} #并无开始作验证 obj = AjaxForm(request.POST) #is_valid作的验证 if obj.is_valid(): print("验证成功",obj.cleaned_data) #ajax没法自动跳转,须要在前端手动设置 return HttpResponse(json.dumps(response)) else: #<class 'django.forms.utils.ErrorDict'>继承dict print("验证失败", obj.errors,type(obj.errors)) response['status'] = False response['msg'] = obj.errors return HttpResponse(json.dumps(response))
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> {% load staticfiles %} </head> <body> <form action="/ajax/" method="POST" novalidate id="fm"> {% csrf_token %} {{ obj.as_p }} <input type="button" value="ajax提交" id="ajaxSubmit"/> </form> <script src="{% static 'js/jquery-1.12.4.js' %}"></script> <script> $(function () { $('#ajaxSubmit').click(function () { $.ajax({ url:'/ajax/', type:'POST', data:$('#fm').serialize(), dataType:'JSON', success:function (arg) { if(arg.status){ window.location.href = 'http://www.baidu.com'; } console.log(arg); } }) }) }) </script> </body> </html>
八、在根据源码流程自定义方法
from django.core.exceptions import ValidationError class AjaxForm(forms.Form): username = fields.CharField() user_id = fields.IntegerField( widget=widgets.Select(choices=[(1, '中国'), (2, '美国'), ],) ) #自定义方法clean_字段名, #必须返回值self.cleaned_data['username'] #若是出错:raise ValidationError('用户名已存在') def clean_username(self): v = self.cleaned_data['username'] if UserInfo.objects.filter(username=v).count(): #若是用户信息中有一个字段错了,总体就错误,显示具体错误的详细信息 raise ValidationError('用户名已存在') return v def clean_user_id(self): return self.cleaned_data['user_id']
def clean(self): ''' obj.errors错误信息 { __all__:[], username:[], user_id:[], } ''' # Django的总体错误信息放在__all__中 v1 = self.cleaned_data.get('username') v2 = self.cleaned_data.get('user_id') if v1=='charlie' and v2==1: raise ValidationError('总体错误信息') return self.cleaned_data
from django.core.exceptions import NON_FIELD_ERRORS #这里添加错误的时候k就是None self.add_error(None, e) #可是None又被替换成__all__,因此前端须要经过.all来取 NON_FIELD_ERRORS = '__all__' # js代码 console.log(arg.msg.__all__); #HTML模板 #模板语言里不支持双下划线格式,因此用obj.non_field_errors.0
九、is_valid()验证表单时,一共给咱们留了三个钩子
# 自定义方法名必须是clean_字段名 def _clean_fields(self): for name, field in self.fields.items(): # value_from_datadict() gets the data from the data dictionaries. # Each widget type knows how to retrieve its own data, because some # widgets split data over several HTML fields. if field.disabled: value = self.get_initial_for_field(field, name) else: value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name)) try: if isinstance(field, FileField): initial = self.get_initial_for_field(field, name) value = field.clean(value, initial) else: value = field.clean(value) self.cleaned_data[name] = value if hasattr(self, 'clean_%s' % name): value = getattr(self, 'clean_%s' % name)() self.cleaned_data[name] = value except ValidationError as e: self.add_error(name, e) # 同时验证多个字段或者是联合字段,返回总体错误信息,放在__all__中 def _clean_form(self): try: cleaned_data = self.clean() except ValidationError as e: self.add_error(None, e) else: if cleaned_data is not None: self.cleaned_data = cleaned_data # 没有捕捉异常的代码,因此这个方法不能出错误 def _post_clean(self): """ An internal hook for performing additional cleaning after form cleaning is complete. Used for model validation in model forms. """ pass
十、扩展:ModelForm
在使用Model和Form时,都须要对字段进行定义并指定类型,经过ModelForm则能够省去From中字段的定义
from django import forms from crm import models class CustomerForm(forms.ModelForm): class Meta: model = models.CustomerInfo # fields = ['name','consultant','status'] # 全部字段 fields = '__all__' widgets = { 'email' : forms.PasswordInput(attrs={'class':"alex"}), }
实例:
# forms.py class EnrollmentForm(forms.ModelForm): """审核学员报名信息""" def __new__(cls, *args, **kwargs): # cls.base_fields获取数据库表里的全部字段名 for field_name in cls.base_fields: field_obj = cls.base_fields.get(field_name) # 定制生成的标签的class属性 field_obj.widget.attrs.update({'class': 'form-control'}) # 只读标签不可编辑 if field_name in cls.Meta.readonly_fields: field_obj.widget.attrs.update({'disabled': 'true'}) return forms.ModelForm.__new__(cls) class Meta: model = models.StudentEnrollment fields = '__all__' exclude = ['contract_approved_date'] readonly_fields = ['contract_agreed',] def clean(self): """负责验证disabled字段的值是否被改动了""" if self.errors: # 全局的错误信息,经过customer_form.errors调用 raise forms.ValidationError(("Please fix errors before re-submit.")) if self.instance.id is not None: # 说明这是一个被修改过的表单,须要验证disabled字段 for field in self.Meta.readonly_fields: old_field_val = getattr(self.instance,field) # 数据库里的数据 form_val = self.cleaned_data.get(field) # 表单里提交的数据 if old_field_val != form_val: # 给单个字段添加错误信息 self.add_error(field,"Readonly Field:field should be '{value}',not'{new_value}'". format(**{'value':old_field_val,'new_value':form_val}))
from django.shortcuts import render,HttpResponse,redirect from django.contrib.auth.decorators import login_required # 必须登录才能看到页面 from crm import forms @login_required def contract_audit(request,enrollment_id): '''学员报名审核页''' enrollment_obj = models.StudentEnrollment.objects.filter(id=enrollment_id).first() if request.method == 'POST': enrollment_form = forms.EnrollmentForm(instance=enrollment_obj,data=request.POST) if enrollment_form.is_valid(): enrollment_form.save() # 报名成功后,把学员加入数据库,stu_obj = (<Student: 铁锤>, True) stu_obj = models.Student.objects.get_or_create(customer=enrollment_obj.customer)[0] # 加入学生表 stu_obj.class_grades.add(enrollment_obj.class_grade_id) # 加入班级表 stu_obj.customer.status = 1 # 修改状态 stu_obj.save() # 修改报名表,是否审核经过,和审核经过时间 enrollment_obj.contract_approved = True enrollment_obj.contract_approved_date = datetime.now() enrollment_obj.save() # 给用户发邮件 return redirect('/kingadmin/crm/customerinfo/%s/change/'%enrollment_obj.customer.id) else: # 建立form表单,发到前端 customer_form = forms.CustomerForm(instance=enrollment_obj.customer) enrollment_form = forms.EnrollmentForm(instance=enrollment_obj) return render(request,'crm/contract_audit.html',locals())
cookie的工做原理是:由服务器产生内容,浏览器收到请求后保存在本地;当浏览器再次访问时,浏览器会自动带上cookie,这样服务器就能经过cookie的内容来判断这个是“谁”了。
一、获取Cookie:
request.COOKIES['key'] request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None) 参数: default: 默认值 salt: 加密盐 max_age: 后台控制过时时间
二、设置Cookie:
rep = HttpResponse(...) 或 rep = render(request, ...) rep.set_cookie(key,value,...) rep.set_signed_cookie(key,value,salt='加密盐',...) 参数: key, 键 value='', 值 max_age=None, 超时时间 expires=None, 超时时间(IE requires expires, so set it if hasn't been already.) path='/', Cookie生效的路径,/ 表示根路径,特殊的:跟路径的cookie能够被任何url的页面访问 domain=None, Cookie生效的域名 secure=False, https传输 httponly=False 只能http协议传输,没法被JavaScript获取(不是绝对,底层抓包能够获取到也能够被覆盖)
<script src='/static/js/jquery.cookie.js'></script> $.cookie("list_pager_num", 30,{ path: '/' });
三、应用
from django.shortcuts import render,redirect import datetime def login(request): if request.method == 'POST': name = request.POST.get('user') pwd = request.POST.get('pwd') if name == 'charlie' and pwd == '123': # redirect render 都有返回值,是一个字典,里面有键值对'sessionid':'5225rffg5hh5' ret = redirect('/index/') #给cookie添加一个键值对,下次再来发送请求的时候会带着它来, #若是和这个同样,就不用从新登录了,前提是在同一个客户端登录 #设置cookie有效时间5秒 ret.set_cookie('username',name,max_age=5) #设置有效期三天 ret.set_cookie('username',name,expires=datetime.datetime.utcnow() +datetime.timedelta(days=3)) return ret return render(request,'login.html') def index(request): if request.COOKIES.get('username',None): name = request.COOKIES.get('username',None) return render(request,'index.html',locals()) else: return redirect('/login/')
cookie虽然在必定程度上解决了“保持状态”的需求,可是因为cookie自己最大支持4096字节,以及cookie自己保存在客户端,可能被拦截或窃取,所以就须要有一种新的东西,它能支持更多的字节,而且他保存在服务器,有较高的安全性。这就是session(session默认在服务器端保存15天)。
Django中默认支持Session,其内部提供了5种类型的Session供开发者使用:
一、数据库Session
Django默认支持Session,而且默认是将Session数据存储在数据库中,即:django_session 表中。 a. 配置 settings.py SESSION_ENGINE = 'django.contrib.sessions.backends.db' # 引擎(默认) SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认) SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径(默认) SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名(默认) SESSION_COOKIE_SECURE = False # 是否Https传输cookie(默认) SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输(默认) SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周)(默认) SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过时(默认) SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改以后才保存(默认) b. 使用 def index(request): # 获取、设置、删除Session中数据 request.session['k1'] request.session.get('k1',None) request.session['k1'] = 123 request.session.setdefault('k1',123) # 存在则不设置 del request.session['k1'] # 全部 键、值、键值对 request.session.keys() request.session.values() request.session.items() request.session.iterkeys() request.session.itervalues() request.session.iteritems() # 用户session的随机字符串 request.session.session_key # 将全部Session失效日期小于当前日期的数据删除 request.session.clear_expired() # 检查 用户session的随机字符串 在数据库中是否 request.session.exists("session_key") # 删除当前用户的全部Session数据 request.session.delete("session_key") request.session.set_expiry(value) * 若是value是个整数,session会在些秒数后失效。 * 若是value是个datatime或timedelta,session就会在这个时间后失效。 * 若是value是0,用户关闭浏览器session就会失效。 * 若是value是None,session会依赖全局session失效策略。
二、缓存Session
a. 配置 settings.py SESSION_ENGINE = 'django.contrib.sessions.backends.cache' # 引擎 SESSION_CACHE_ALIAS = 'default' # 使用的缓存别名(默认内存缓存,也能够是memcache),此处别名依赖缓存的设置 SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串 SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径 SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名 SESSION_COOKIE_SECURE = False # 是否Https传输cookie SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输 SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周) SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过时 SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改以后才保存 b. 使用同上
三、文件Session
a. 配置 settings.py SESSION_ENGINE = 'django.contrib.sessions.backends.file' # 引擎 SESSION_FILE_PATH = None # 缓存文件路径,若是为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir() # 如:/var/folders/d3/j9tj0gz93dg06bmwxmhh6_xm0000gn/T SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串 SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径 SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名 SESSION_COOKIE_SECURE = False # 是否Https传输cookie SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输 SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周) SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过时 SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改以后才保存 b. 使用同上
四、缓存+数据库Session
数据库用于作持久化,缓存用于提升效率 a. 配置 settings.py SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db' # 引擎 b. 使用同上
五、加密cookie Session
a. 配置 settings.py SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies' # 引擎 b. 使用同上
六、cookie 和Session的结合使用的两种方式
存储在服务端:经过cookie存储一个session_id,而后具体的数据则是保存在session中。若是用户已经登陆,则服务器会在cookie中保存一个session_id,下次再次请求的时候,会把该session_id携带上来,服务器根据session_id在session库中获取用户的session数据。就能知道该用户究竟是谁,以及以前保存的一些状态信息。这种专业术语叫作server side session。
将session数据加密,而后存储在cookie中。这种专业术语叫作client side session。flask采用的就是这种方式,可是也能够替换成其余形式。
七、应用
数据库session,须要执行python manage.py makemigrations命令,须要Django帮咱们建立session表,用来保存session内容,不然会报错;
cookie是一个字典,里面默认有两个键值对:‘sessionid’,‘csrftoken’,session是一个字典对象,两个均可以用request.session['username']=name方法来添加键值对;
删除session:del request.session[key]
from django.shortcuts import render,redirect import datetime def login(request): if request.method == 'POST': name = request.POST.get('user') pwd = request.POST.get('pwd') if name == 'charlie' and pwd == '123': #cookie + session request.session['is_login']=True request.session['user']=name return redirect('/index/') return render(request,'login.html') def index(request): #cookie + session,加上None防止找不到报错 if request.session.get('is_login',None): name = request.session.get('user',None) return render(request,'index.html',locals()) else: return redirect('/login/')
一、代码实现简单的上一页、下一页
#index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <ul> {% for row in user_list %} <li>{{ row.name }} - {{ row.age }}</li> {% endfor %} </ul> <a href="/index.html?p={{ prev_page }}">上一页</a> <a href="/index.html?p={{ next_page }}">下一页</a> </body> </html> #views.py from django.shortcuts import render USER_LIST = [] for i in range(1,1000): temp = {'name':'root' + str(i),'age':i} USER_LIST.append(temp) def index(request): #简单的分页 per_page_count = 10 current_page = int(request.GET.get('p')) #p=1 索引0-10 #p=2 索引10-20 #http://127.0.0.1:8005/index.html?p=1 start = (current_page-1)*per_page_count end = current_page*per_page_count user_list = USER_LIST[start:end] if current_page <= 1: prev_page = 1 next_page = current_page + 1 else: prev_page = current_page - 1 next_page = current_page + 1 return render(request,'index.html',locals())
二、Django内置分页+扩展
-- Django自带的内置分页只能显示上一页和下一页按钮,不能显示中间的页码,因此这里须要扩展一下
from django.shortcuts import render from django.core.paginator import Paginator,EmptyPage,PageNotAnInteger USER_LIST = [] for i in range(1,230): temp = {'name':'root' + str(i),'age':i} USER_LIST.append(temp) #扩展自带分页 class CustomPaginator(Paginator): def __init__(self,current_page,per_pager_num,*args,**kwargs): #当前页 self.current_page = int(current_page) #每页最多显示多少页码 self.per_pager_num = int(per_pager_num) super(CustomPaginator,self).__init__(*args,**kwargs) def page_num_range(self): #若是总页数小于每页最多显示页码数量,就显示1-总页码 if self.num_pages < self.per_pager_num: return range(1,self.num_pages+1) #若是总页数有不少 part = int(self.per_pager_num/2) if self.current_page <= part: return range(1,self.per_pager_num+1) #最后一页只显示最后10个页码便可 if (self.current_page+part) > self.num_pages: return range(self.num_pages-self.per_pager_num+1,self.num_pages+1) return range(self.current_page-part,self.current_page+part+1) def index1(request): #django自带分页 current_page = request.GET.get('p') #Paginator对象,每页显示10个页码,10条数据 paginator = CustomPaginator(current_page,11,USER_LIST,10) try: #Page对象 posts = paginator.page(current_page) except PageNotAnInteger: #若是输入不是数字,就显示第一页 posts = paginator.page(1) except EmptyPage: #若是输入为空或数字过大,就显示最后一页 posts = paginator.page(paginator.num_pages) return render(request,'index1.html',locals())
#注意:这里使用include引用了文件 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <ul> {% for row in posts %} <li>{{ row.name }} - {{ row.age }}</li> {% endfor %} </ul> {% include 'include/pager.html' %} </body> </html>
<a href="/index1.html?p=1">首页</a> {% if posts.has_previous %} <a href="/index1.html?p={{ posts.previous_page_number }}">上一页</a> {% endif %} {% for i in paginator.page_num_range %} {% if i == posts.number %} <a style="font-size: 20px;color: blue;" href="/index1.html?p={{ i }}">{{ i }}</a> {% else %} <a href="/index1.html?p={{ i }}">{{ i }}</a> {% endif %} {% endfor %} {% if posts.has_next %} <a href="/index1.html?p={{ posts.next_page_number }}">下一页</a> {% endif %} <span> [{{ posts.number }}/{{ paginator.num_pages }}] </span> <a href="/index1.html?p={{ paginator.num_pages }}">尾页</a>
二、自定义分页
— 分页功能在每一个网站都是必要的,对于分页来讲,其实就是根据用户的输入计算出应该在数据库表中的起始位置,而且须要在页面上显示分页的页面。如:[上一页][1][2][3][4][5][下一页]
一、设定每页显示数据条数
二、用户输入页码(第一页、第二页...)
三、设定显示多少页号
四、获取当前数据总条数
五、根据设定显示多少页号和数据总条数计算出,总页数
六、根据设定的每页显示条数和当前页码,计算出须要取数据表的起始位置
七、在数据表中根据起始位置取值,页面上输出数据
八、输出分页html,如:[上一页][1][2][3][4][5][下一页]
实例:在APP中建立pager.py
#在APP中建立pager.py class Pagination(object): def __init__(self,total_count,current_page,per_page_item_num=20,max_page_num=7): #数据总条目 self.total_count = total_count #当前页 try: v = int(current_page) if v <= 0: v = 1 self.current_page = v except Exception as e: self.current_page = 1 #每页显示条目 self.per_page_item_num = per_page_item_num #每页最多显示页码 self.max_page_num = max_page_num def start(self): return (self.current_page-1)*self.per_page_item_num def end(self): return self.current_page*self.per_page_item_num @property def num_pages(self): # 总页数 a,b = divmod(self.total_count,self.per_page_item_num) if b == 0: return a return a+1 def page_num_range(self): #若是总页数小于每页最多显示页码数量,就显示1-总页码 if self.num_pages < self.max_page_num: return range(1,self.num_pages+1) #若是总页数有不少 part = int(self.max_page_num/2) if self.current_page <= part: return range(1,self.max_page_num+1) #最后一页只显示最后10个页码便可 if (self.current_page+part) > self.num_pages: return range(self.num_pages-self.max_page_num+1,self.num_pages+1) return range(self.current_page-part,self.current_page+part+1) def page_str(self): page_list = [] first_page = '<li><a href="/index2.html?p=1">首页</a></li>' page_list.append(first_page) if self.current_page == 1: prev_page = '<li><a href="#">上一页</a>' else: prev_page = '<li><a href="/index2.html?p=%s">上一页</a></li>'%(self.current_page-1) page_list.append(prev_page) for i in self.page_num_range(): if i == self.current_page: temp = '<li class="active"><a href="/index2.html?p=%s">%s</a></li>' % (i, i) else: temp = '<li><a href="/index2.html?p=%s">%s</a></li>'%(i,i) page_list.append(temp) if self.current_page == self.num_pages: next_page = '<li><a href="#">下一页</a></li>' else: next_page = '<li><a href="/index2.html?p=%s">下一页</a></li>' % (self.current_page + 1) page_list.append(next_page) last_page = '<li><a href="/index2.html?p=%s">尾页</a></li>'%self.num_pages page_list.append(last_page) return ''.join(page_list)
from django.shortcuts import render from django.core.paginator import Paginator,EmptyPage,PageNotAnInteger USER_LIST = [] for i in range(1,600): temp = {'name':'root' + str(i),'age':i} USER_LIST.append(temp) def index2(request): from app.pager import Pagination current_page = request.GET.get('p') page_obj = Pagination(600,current_page) data = USER_LIST[page_obj.start():page_obj.end()] return render(request,'index2.html',locals())
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.css"/> </head> <body> <ul> {% for row in data %} <li>{{ row.name }} - {{ row.age }}</li> {% endfor %} </ul> <ul class="pagination"> {{ page_obj.page_str|safe }} </ul> </body> </html>
总结,分页时须要作三件事:
建立处理分页数据的类
根据分页数据获取数据
输出分页HTML,即:[上一页][1][2][3][4][5][下一页]
序列化就是把对象转换成能够保存在本地文件中的数据类型,关于Django中的序列化主要应用在将数据库中检索的数据返回给客户端用户,特别的Ajax请求通常返回的为Json格式。
一、Python中dumps就是序列化,loads就是反序列化;
二、JavaScript中
对象转换成字符串
-
-
str
=
JSON.stringify({
'k'
:
'v'
})
字符串转换成对象
-
-
dict
=
JSON.parse(
str
)
三、三种状况
QuerySet内部是:
对象(用all,filter获取数据):serializers.serializer('json',QuerySet)
字典(用values获取):list(QuerySet)
元组(用values_list获取):list(QuerySet)
from django.core import serializers
import json def get_data(request): response = {'status': True, 'data': None} try: user_list = UserInfo.objects.all() # serializers只能序列化queryset对象,转换为字符串 response['data'] = serializers.serialize('json', user_list) #若是queryset内部是字典或元组,values和values_list获取,道理同样 user_list = UserInfo.objects.values('id','username') #只须要把外部的queryset变成列表形式,就能够直接json序列化了 response['data'] = list(user_list) except Exception as e: response['status'] = False ret = json.dumps(response) return HttpResponse(ret)
四、因为json.dumps时没法处理datetime日期,因此能够经过自定义处理器来作扩展,如:
import json from datetime import date from datetime import datetime class JsonCustomEncoder(json.JSONEncoder): def default(self, field): if isinstance(field, datetime): return field.strftime('%Y-%m-%d %H:%M:%S') elif isinstance(field, date): return field.strftime('%Y-%m-%d') elif isinstance(field, Response): return field.__dict__ # 对象以字典的方式输出 else: return json.JSONEncoder.default(self, field) class Response: def __init__(self): self.status = True self.data = 'charlie' data = { 'k1': datetime.now(), 'k2': Response(), } ds = json.dumps(data, cls=JsonCustomEncoder) print(ds) # {"k1": "2019-03-08 16:18:28", "k2": {"status": true, "data": "charlie"}}